lists
what is a list?
A list is a container object that can hold any objects
they are represented with
[]they can be made with the list constructor (
list())they can be changed with an operation, which means they are mutable
preview
Here are the tests I have by the end of the chapter
1import unittest
2
3
4class TestLists(unittest.TestCase):
5
6 def test_making_a_list(self):
7 self.assertEqual(list(), [])
8 self.assertEqual(list((0, 1, 2, 'n')), [0, 1, 2, 'n'])
9
10 def test_attributes_and_methods_of_lists(self):
11 self.maxDiff = None
12 self.assertEqual(
13 dir(list),
14 [
15 '__add__',
16 '__class__',
17 '__class_getitem__',
18 '__contains__',
19 '__delattr__',
20 '__delitem__',
21 '__dir__',
22 '__doc__',
23 '__eq__',
24 '__format__',
25 '__ge__',
26 '__getattribute__',
27 '__getitem__',
28 '__getstate__',
29 '__gt__',
30 '__hash__',
31 '__iadd__',
32 '__imul__',
33 '__init__',
34 '__init_subclass__',
35 '__iter__',
36 '__le__',
37 '__len__',
38 '__lt__',
39 '__mul__',
40 '__ne__',
41 '__new__',
42 '__reduce__',
43 '__reduce_ex__',
44 '__repr__',
45 '__reversed__',
46 '__rmul__',
47 '__setattr__',
48 '__setitem__',
49 '__sizeof__',
50 '__str__',
51 '__subclasshook__',
52 'append',
53 'clear',
54 'copy',
55 'count',
56 'extend',
57 'index',
58 'insert',
59 'pop',
60 'remove',
61 'reverse',
62 'sort',
63 ]
64 )
65
66 def test_append_adds_item_to_end_of_a_list(self):
67 a_list = [0, 1, 2, 'n']
68 self.assertIsNone(a_list.append('n+1'))
69 self.assertEqual(a_list, [0, 1, 2, 'n', 'n+1'])
70
71 def test_clear_empties_a_list(self):
72 a_list = [0, 1, 2, 'n']
73 self.assertIsNone(a_list.clear())
74 self.assertEqual(a_list, [])
75
76 def test_copy_a_list(self):
77 a_list = [0, 1, 2, 'n']
78 self.assertEqual(a_list.copy(), [0, 1, 2, 'n'])
79
80 def test_count_number_of_times_item_is_in_a_list(self):
81 a_list = [0, 1, 2, 1, 'n', 1]
82 self.assertEqual(a_list.count(0), 1)
83 self.assertEqual(a_list.count(1), 3)
84 self.assertEqual(a_list.count('not in list'), 0)
85
86 def test_extend_adds_items_from_an_iterable_to_end_of_a_list(self):
87 a_list = [0, 1, 2, 'n']
88 self.assertIsNone(a_list.extend((2, 1, 0)))
89 self.assertEqual(a_list, [0, 1, 2, 'n', 2, 1, 0])
90
91 def test_index_returns_first_position_of_item_in_a_list(self):
92 a_list = ['1st', '2nd', '3rd', '...last', '1st']
93 self.assertEqual(a_list.index('1st', 0), 0)
94 self.assertEqual(a_list.index('3rd', 0), 2)
95 self.assertEqual(a_list.index('2nd', 0), 1)
96 self.assertEqual(a_list.index('...last', 0), 3)
97 self.assertEqual(a_list.index('1st', 1), 4)
98
99 with self.assertRaises(ValueError):
100 a_list.index('not in list')
101
102 def test_insert_item_at_position_in_a_list(self):
103 a_list = [0, 1, 2, 'n']
104 self.assertIsNone(a_list.insert(0, -1))
105 self.assertEqual(a_list, [-1, 0, 1, 2, 'n'])
106 self.assertIsNone(a_list.insert(3, 1.5))
107 self.assertEqual(a_list, [-1, 0, 1, 1.5, 2, 'n'])
108
109 def test_pop_removes_and_returns_last_item_from_a_list(self):
110 a_list = [0, 1, 2, 'n']
111 self.assertEqual(a_list.pop(), 'n')
112 self.assertEqual(a_list, [0, 1, 2])
113 self.assertEqual(a_list.pop(), 2)
114 self.assertEqual(a_list, [0, 1])
115
116 def test_remove_first_time_item_is_in_a_list(self):
117 a_list = [0, 1, 0, 2, 0, 'n']
118 self.assertIsNone(a_list.remove(0))
119 self.assertEqual(a_list, [1, 0, 2, 0, 'n'])
120
121 with self.assertRaises(ValueError):
122 a_list.remove('not in list')
123
124 def test_reverse_a_list(self):
125 a_list = [0, 1, 2, 'n']
126 self.assertIsNone(a_list.reverse())
127 self.assertEqual(a_list, ['n', 2, 1, 0])
128
129 def test_sort_a_list(self):
130 with self.assertRaises(TypeError):
131 [0, 1, 2, 'n'].sort()
132
133 a_list = [0, 1, -1, 2, -2, 3, -3]
134 self.assertIsNone(a_list.sort())
135 self.assertEqual(a_list, [-3, -2, -1, 0, 1, 2, 3])
136
137 def test_getting_items_of_a_list(self):
138 a_list = ['1st', '2nd', '3rd', '...last']
139 self.assertEqual(a_list[0], '1st')
140 self.assertEqual(a_list[a_list.index('1st')], '1st')
141 self.assertEqual(a_list[-4], '1st')
142 self.assertEqual(a_list[2], '3rd')
143 self.assertEqual(a_list[-2], '3rd')
144 self.assertEqual(a_list[1], '2nd')
145 self.assertEqual(a_list[-3], '2nd')
146 self.assertEqual(a_list[3], '...last')
147 self.assertEqual(a_list[-1], '...last')
148
149 def test_setting_items_in_a_list(self):
150 a_list = ['1st', '2nd', '3rd', '...last']
151 a_list[-1] = '4th'
152 self.assertEqual(a_list, ['1st', '2nd', '3rd', '4th'])
153
154 def test_looking_at_parts_of_a_list_aka_slicing(self):
155 a_list = ['a', 'b', 'c', 'd']
156 self.assertEqual(a_list[0:2], ['a', 'b'])
157 self.assertEqual(a_list[:2], ['a', 'b'])
158 self.assertEqual(a_list[1:4], ['b', 'c', 'd'])
159 self.assertEqual(a_list[1:], ['b', 'c', 'd'])
160 self.assertEqual(a_list[0:3], ['a', 'b', 'c'])
161 self.assertEqual(a_list[1:3], ['b', 'c'])
162 self.assertEqual(a_list[:], a_list.copy())
163
164 def test_index_error(self):
165 a_list = ['a', 'b', 'c', 'd']
166
167 with self.assertRaises(IndexError):
168 a_list[4]
169 with self.assertRaises(IndexError):
170 a_list[-5]
171 with self.assertRaises(IndexError):
172 [].pop()
173 with self.assertRaises(IndexError):
174 [][-1]
175
176
177# Exceptions seen
178# AssertionError
179# TypeError
180# ValueError
181# IndexError
questions about lists
Here are questions you can answer after going through this chapter
start the project
I name this project
listsI open
makePythonTdd.shormakePythonTdd.ps1in the editorTip
Here is a quick way to open
makePythonTdd.shormakePythonTdd.ps1if you are using Visual Studio Codecode makePythonTdd.shon Windows without Windows Subsystem for Linux use
code makePythonTdd.ps1I change everywhere I have
type_errorto the name of this project inmakePythonTdd.sh1#!/bin/bash 2mkdir lists 3cd lists 4mkdir src 5touch src/lists.py 6mkdir tests 7touch tests/__init__.py 8 9echo "import unittest 10 11 12class TestLists(unittest.TestCase): 13 14 def test_failure(self): 15 self.assertFalse(True) 16 17 18# Exceptions seen 19# AssertionError 20" > tests/test_lists.pyAttention
on Windows without Windows Subsystem for Linux use
makePythonTdd.ps1instead ofmakePythonTdd.sh1mkdir lists 2cd lists 3mkdir src 4New-Item src/lists.py 5mkdir tests 6New-Item tests/__init__.py 7 8"import unittest 9 10 11class TestLists(unittest.TestCase): 12 13 def test_failure(self): 14 self.assertFalse(True) 15 16# Exceptions seen 17# AssertionError 18" | Out-File tests/test_lists.pyI run the program in the terminal
./makePythonTdd.shAttention
on Windows without Windows Subsystem for Linux use
makePythonTdd.ps1instead ofmakePythonTdd.sh./makePythonTdd.ps1the terminal shows AssertionError
======================================= FAILURES ======================================= ________________________________ TestLists.test_failure ________________________________ self = <tests.test_lists.TestLists testMethod=test_failure> def test_failure(self): > self.assertFalse(True) E AssertionError: True is not false tests/test_lists.py:7: AssertionError =============================== short test summary info ================================ FAILED tests/test_lists.py::TestLists::test_failure - AssertionError: True is not false ================================== 1 failed in X.YZs ===================================I hold ctrl (Windows/Linux) or option or command (MacOS) on the keyboard and use the mouse to click on
tests/test_lists.py:7to open it in the editor-
7 self.assertFalse(False)the test passes
test_making_a_list
RED: make it fail
I change test_failure to test_making_a_list
4class TestLists(unittest.TestCase):
5
6 def test_making_a_list(self):
7 self.assertEqual(list(), None)
the terminal shows AssertionError
AssertionError: [] != None
GREEN: make it pass
I change the expectation
7 self.assertEqual(list(), [])
the test passes. I can make an empty list with list() or []
REFACTOR: make it better
I add another assertion, this time with input to the list constructor, I want to make a list that has things in it
7 self.assertEqual(list(), []) 8 self.assertEqual(list(0), [])TypeError: 'int' object is not iterableI add TypeError to the list of Exceptions seen
11# Exceptions seen 12# AssertionError 13# TypeError
what is an iterable?
An iterable is an object that can return what it has, one at a time. In order words, I can loop over it. strings, tuples, lists, sets and dictionaries are iterable
I change the input to a tuple (anything in parentheses (
()), separated by a comma)7 self.assertEqual(list(), []) 8 self.assertEqual(list((0, 1, 2, 'n')), [])the terminal shows AssertionError
AssertionError: Lists differ: [0, 1, 2, 'n'] != []I change the expectation to match
8 self.assertEqual(list((0, 1, 2, 'n')), [0, 1, 2, 'n'])the test passes.
I can make a list with the constructor (list()) or square brackets([]) which uses less characters
test_attributes_and_methods_of_lists
how to see the the attributes and methods of an object
I want to test the things I can do with lists. I can use the dir function to see the attributes and methods of objects, it is part of Python’s Built-in Functions
RED: make it fail
I add a failing test
8 self.assertEqual(list(0, 1, 2, 'n'), [0, 1, 2, 'n'])
9
10 def test_attributes_and_methods_of_lists(self):
11 self.assertEqual(
12 dir(list),
13 []
14 )
the terminal shows AssertionError
AssertionError: Lists differ: ['__add__', '__class__', '__class_getitem_[552 chars]ort'] != []
there is also a note on how to see the full difference between dir(list) and my empty list
Diff is 748 characters long. Set self.maxDiff to None to see it
maxDiff is an attribute of the unittest.TestCase class that sets the maximum number of characters to show when comparing 2 objects in the terminal, when it is set to None it shows the full difference
GREEN: make it pass
I add
self.maxDiffto the test then move the terminal to the right10 def test_attributes_and_methods_of_lists(self): 11 self.maxDiff = None 12 self.assertEqual( 13 dir(list), 14 [] 15 )the terminal shows a long list of items. I copy (ctrl/command+c) and paste (ctrl/command+v) them from the terminal then use find and replace to remove the extra characters
Eand-Note
results can be different because of the Python version
10 def test_attributes_and_methods_of_lists(self): 11 self.maxDiff = None 12 self.assertEqual( 13 dir(list), 14 [ 15 '__add__', 16 '__class__', 17 '__class_getitem__', 18 '__contains__', 19 '__delattr__', 20 '__delitem__', 21 '__dir__', 22 '__doc__', 23 '__eq__', 24 '__format__', 25 '__ge__', 26 '__getattribute__', 27 '__getitem__', 28 '__getstate__', 29 '__gt__', 30 '__hash__', 31 '__iadd__', 32 '__imul__', 33 '__init__', 34 '__init_subclass__', 35 '__iter__', 36 '__le__', 37 '__len__', 38 '__lt__', 39 '__mul__', 40 '__ne__', 41 '__new__', 42 '__reduce__', 43 '__reduce_ex__', 44 '__repr__', 45 '__reversed__', 46 '__rmul__', 47 '__setattr__', 48 '__setitem__', 49 '__sizeof__', 50 '__str__', 51 '__subclasshook__', 52 'append', 53 'clear', 54 'copy', 55 'count', 56 'extend', 57 'index', 58 'insert', 59 'pop', 60 'remove', 61 'reverse', 62 'sort' 63 ] 64 )the test passes and I move the terminal back to the bottom
I copy (ctrl/command+c) and paste (ctrl/command+v) the names that do NOT have double underscores (__) to use as a TODO list
67'append', 68'clear', 69'copy', 70'count', 71'extend', 72'index', 73'insert', 74'pop', 75'remove', 76'reverse', 77'sort' 78 79 80# Exceptions seen 81# AssertionError 82# TypeError
test_append_adds_item_to_end_of_a_list
RED: make it fail
I add a test for the append method
62 'sort'
63 ]
64 )
65
66 def test_append(self):
67 a_list = [0, 1, 2, 'n']
68 self.assertIsNone(a_list.append())
69
70
71 'append',
TypeError: list.append() takes exactly one argument (0 given)
GREEN: make it pass
I add 0 as input
68 self.assertIsNone(a_list.append(0))
the terminal shows green, the append method returns None when called
REFACTOR: make it better
I add another assertion to see what append did to the list
68 self.assertIsNone(a_list.append(0)) 69 self.assertEqual(a_list, [0, 1, 2, 'n'])the terminal shows AssertionError
AssertionError: Lists differ: [0, 1, 2, 'n', 0] != [0, 1, 2, 'n']the append method added a value at the end of the list
I change the expectation to match the values in the terminal
69 self.assertEqual(a_list, [0, 1, 2, 'n', 0])the test passes
I change the value given to append
68 self.assertIsNone(a_list.append('n+1'))the terminal shows AssertionError
AssertionError: Lists differ: [0, 1, 2, 'n', 'n+1'] != [0, 1, 2, 'n', 0]I change the expectation to match
69 self.assertEqual(a_list, [0, 1, 2, 'n', 'n+1'])the test passes
I change the name of the test
66 def test_append_adds_item_to_end_of_a_list(self): 67 a_list = [0, 1, 2, 'n'] 68 self.assertIsNone(a_list.append('n+1')) 69 self.assertEqual(a_list, [0, 1, 2, 'n', 'n+1'])I remove append from the TODO list
69 self.assertEqual(a_list, [0, 1, 2, 'n', 'n+1']) 70 71 72'clear', 73'copy', 74'count', 75'extend', 76'index', 77'insert', 78'pop', 79'remove', 80'reverse', 81'sort'
test_clear_empties_a_list
I add a test for the clear method
69 self.assertEqual(a_list, [0, 1, 2, 'n', 'n+1'])
70
71 def test_clear(self):
72 a_list = [0, 1, 2, 'n']
73 self.assertIsNone(a_list.clear())
74
75
76'clear',
the terminal shows green. The clear method returns None when called
RED: make it fail
I add an assertion to see what clear did to the list
73 self.assertIsNone(a_list.clear())
74 self.assertEqual(a_list, [0, 1, 2, 'n'])
the terminal shows AssertionError
AssertionError: Lists differ: [] != [0, 1, 2, 'n']
the list is now empty
GREEN: make it pass
I change the values to match
74 self.assertEqual(a_list, [])
the test passes
REFACTOR: make it better
I change the name of the test to make it “clearer”
71 def test_clear_empties_a_list(self): 72 a_list = [0, 1, 2, 'n'] 73 self.assertIsNone(a_list.clear()) 74 self.assertEqual(a_list, [])I remove clear from the TODO list
74 self.assertEqual(a_list, []) 75 76 77'copy', 78'count', 79'extend', 80'index', 81'insert', 82'pop', 83'remove', 84'reverse', 85'sort'
test_copy_a_list
RED: make it fail
I add another test
74 self.assertEqual(a_list, [])
75
76 def test_copy(self):
77 a_list = [0, 1, 2, 'n']
78 self.assertIsNone(a_list.copy())
79
80
81'copy',
the terminal shows AssertionError
AssertionError: [0, 1, 2, 'n'] is not None
GREEN: make it pass
I add the list to the assertion
77 a_list = [0, 1, 2, 'n'] 78 self.assertIsNone(a_list.copy(), [0, 1, 2, 'n'])the terminal shows AssertionError
AssertionError: [0, 1, 2, 'n'] is not None : [0, 1, 2, 'n']the values are the same, the problem is assertIsNone only takes 1 input and I gave it 2
I change assertIsNone to assertEqual
77 a_list = [0, 1, 2, 'n'] 78 self.assertEqual(a_list.copy(), [0, 1, 2, 'n'])the test passes
REFACTOR: make it better
I change the name of the test
76 def test_copy_a_list(self): 77 a_list = [0, 1, 2, 'n'] 78 self.assertEqual(a_list.copy(), [0, 1, 2, 'n'])I remove copy from the TODO list
78 self.assertIsNone(a_list.copy(), [0, 1, 2, 'n']) 79 80 81'count', 82'extend', 83'index', 84'insert', 85'pop', 86'remove', 87'reverse', 88'sort'
test_count_number_of_times_item_is_in_a_list
RED: make it fail
I add a test for the next method
78 self.assertEqual(a_list.copy(), [0, 1, 2, 'n'])
79
80 def test_count(self):
81 a_list = [0, 1, 2, 'n']
82 self.assertIsNone(a_list.count())
83
84
85'count',
TypeError: list.count() takes exactly one argument (0 given)
I add a value to the call
80 def test_count(self):
81 a_list = [0, 1, 2, 'n']
82 self.assertIsNone(a_list.count(0))
the terminal shows AssertionError
AssertionError: 1 is not None
GREEN: make it pass
I add the value
82 self.assertIsNone(a_list.count(0), 1)
the terminal shows AssertionError
AssertionError: 1 is not None : 1
I change assertIsNone to assertEqual
82 self.assertEqual(a_list.count(0), 1)
the test passes
REFACTOR: make it better
0is in this list 1 time, I add1to the list 2 more times then add an assertion for it80 def test_count(self): 81 a_list = [0, 1, 2, 1, 'n', 1] 82 self.assertEqual(a_list.count(0), 1) 83 self.assertEqual(a_list.count(1), 1)the terminal shows AssertionError
AssertionError: 3 != 1I change the value to match
83 self.assertEqual(a_list.count(1), 3)the test passes
I want to see what happens when I try to count something that is not in the list
83 self.assertEqual(a_list.count(1), 3) 84 self.assertEqual(a_list.count('not in list'), 3)the terminal shows AssertionError
AssertionError: 0 != 3The count method returns
0when the item is not in the listI change the value to match
84 self.assertEqual(a_list.count('not in list'), 0)the test passes
I change the name of the test
80 def test_count_number_of_times_item_is_in_a_list(self): 81 a_list = [0, 1, 2, 1, 'n', 1] 82 self.assertEqual(a_list.count(0), 1) 83 self.assertEqual(a_list.count(1), 3) 84 self.assertEqual(a_list.count('not in list'), 0)I remove count from the TODO list
84 self.assertEqual(a_list.count('not in list'), 0) 85 86 87'extend', 88'index', 89'insert', 90'pop', 91'remove', 92'reverse', 93'sort'
test_extend_adds_items_from_an_iterable_to_end_of_a_list
RED: make it fail
time for another test
84 self.assertEqual(a_list.count('not in list'), 0)
85
86 def test_extend(self):
87 a_list = [0, 1, 2, 'n']
88 self.assertIsNone(a_list.extend())
89
90
91'extend',
TypeError: list.extend() takes exactly one argument (0 given)
GREEN: make it pass
I pass a value to the call
88 self.assertIsNone(a_list.extend(0))
TypeError: 'int' object is not iterable
I change the value to an iterable
88 self.assertIsNone(a_list.extend((0, 1)))
the test passes. The extend method returns None when called
REFACTOR: make it better
I add another assertion to see what it did to the list
88 self.assertIsNone(a_list.extend((0, 1))) 89 self.assertEqual(a_list, [0, 1, 2, 'n'])the terminal shows AssertionError
AssertionError: Lists differ: [0, 1, 2, 'n', 0, 1] != [0, 1, 2, 'n']I change the expectation to match
89 self.assertEqual(a_list, [0, 1, 2, 'n', 0, 1])the test passes
I change the values given to the extend method
86 def test_extend(self): 87 a_list = [0, 1, 2, 'n'] 88 self.assertIsNone(a_list.extend((2, 1, 0))) 89 self.assertEqual(a_list, [0, 1, 2, 'n', 0, 1])the terminal shows AssertionError
AssertionError: Lists differ: [0, 1, 2, 'n', 2, 1, 0] != [0, 1, 2, 'n', 0, 1]I change the values to match
89 self.assertEqual(a_list, [0, 1, 2, 'n', 2, 1, 0])the test is green again, it looks like extend calls append for each item in the iterable
I change the name of the test
86 def test_extend_adds_items_from_an_iterable_to_end_of_a_list(self): 87 a_list = [0, 1, 2, 'n'] 88 self.assertIsNone(a_list.extend((2, 1, 0))) 89 self.assertEqual(a_list, [0, 1, 2, 'n', 2, 1, 0])I remove extend from the TODO list
89 self.assertEqual(a_list, [0, 1, 2, 'n', 2, 1, 0]) 90 91 92'index', 93'insert', 94'pop', 95'remove', 96'reverse', 97'sort'
test_index_returns_first_position_of_item_in_a_list
RED: make it fail
I add a test for the index method
89 self.assertEqual(a_list, [0, 1, 2, 'n', 2, 1, 0])
90
91 def test_index(self):
92 a_list = [0, 1, 2, 'n']
93 self.assertIsNone(a_list.index())
94
95
96'index',
TypeError: index expected at least 1 argument, got 0
GREEN: make it pass
I add a value to the call
91 def test_index(self):
92 a_list = [0, 1, 2, 'n']
93 self.assertIsNone(a_list.index(0))
the terminal shows AssertionError
AssertionError: 0 is not None
I add the expectation
93 self.assertIsNone(a_list.index(0), 0)
the terminal shows AssertionError
AssertionError: 0 is not None : 0
I change assertIsNone to assertEqual
91 def test_index(self):
92 a_list = [0, 1, 2, 'n']
93 self.assertEqual(a_list.index(0), 0)
the test passes
REFACTOR: make it better
maybe the method returned the same value I gave, I change the list to find out
91 def test_index(self): 92 a_list = ['1st', '2nd', '3rd', '...last'] 93 self.assertEqual(a_list.index(0), 0)the terminal shows ValueError
ValueError: 0 is not in listthe index method raises ValueError when the item is not in the list
I add ValueError to the list of Exceptions seen
104# Exceptions seen 105# AssertionError 106# TypeError 107# ValueErrorI remove the things around the call and change the value to be clearer
91 def test_index(self): 92 a_list = ['1st', '2nd', '3rd', '...last'] 93 a_list.index('not in list')the terminal shows ValueError
ValueError: 'not in list' is not in listI add assertRaises to handle the Exception
91 def test_index(self): 92 a_list = ['1st', '2nd', '3rd', '...last'] 93 94 with self.assertRaises(ValueError): 95 a_list.index('not in list')the test is green again
I add a new assertion
91 def test_index(self): 92 a_list = ['1st', '2nd', '3rd', '...last'] 93 self.assertEqual(a_list.index('1st'), '1st') 94 95 with self.assertRaises(ValueError): 96 a_list.index('not in list')the terminal shows AssertionError
AssertionError: 0 != '1st'I change the expectation
93 self.assertEqual(a_list.index('1st'), 0)the test passes. The index method does not just return the value I give it
I add another assertion
91 def test_index(self): 92 a_list = ['1st', '2nd', '3rd', '...last'] 93 self.assertEqual(a_list.index('1st'), 0) 94 self.assertEqual(a_list.index('3rd'), 0)the terminal shows AssertionError
AssertionError: 2 != 0I change the value in the test
94 self.assertEqual(a_list.index('3rd'), 2)the test passes
I add another assertion
94 self.assertEqual(a_list.index('3rd'), 2) 95 self.assertEqual(a_list.index('2nd'), 2) 96 97 with self.assertRaises(ValueError): 98 a_list.index('not in list')the terminal shows AssertionError
AssertionError: 1 != 2I change the value to match
95 self.assertEqual(a_list.index('2nd'), 1)the test is green again
I add another assertion
95 self.assertEqual(a_list.index('2nd'), 1) 96 self.assertEqual(a_list.index('...last'), 1)the terminal shows AssertionError
AssertionError: 3 != 1I change the value to match the terminal
96 self.assertEqual(a_list.index('...last'), 3)the test passes. The index method returns numbers for the position of the item in the list. Python uses zero-based indexing which means the first item has an index of
0and the last item has an index of the length of the list minus1I want to know what happens when I have the same item in the list more than once, so I add something in the list again
91 def test_index(self): 92 a_list = ['1st', '2nd', '3rd', '...last', '1st']the terminal still shows green
I add an assertion
96 self.assertEqual(a_list.index('...last'), 3) 97 self.assertEqual(a_list.index('1st'), 4)the terminal shows AssertionError
AssertionError: 0 != 4when I first called the index method, the terminal showed TypeError
TypeError: index expected at least 1 argument, got 0which means I should be able to send more than one argument
I add a second argument
97 self.assertEqual(a_list.index('1st', 0), 4)the terminal still shows AssertionError
AssertionError: 0 != 4I change the argument
97self.assertEqual(a_list.index('1st', 1), 4)the test passes, the second input is the position I want the method to start from
I try the same thing with the other assertions
91 def test_index(self): 92 a_list = ['1st', '2nd', '3rd', '...last', '1st'] 93 self.assertEqual(a_list.index('1st', 0), 0) 94 self.assertEqual(a_list.index('3rd', 0), 2) 95 self.assertEqual(a_list.index('2nd', 0), 1) 96 self.assertEqual(a_list.index('...last', 0), 3) 97 self.assertEqual(a_list.index('1st', 1), 4) 98 99 with self.assertRaises(ValueError): 100 a_list.index('not in list')the test is still green
I change the name of the test
91 def test_index_returns_first_position_of_item_in_a_list(self): 92 a_list = ['1st', '2nd', '3rd', '...last', '1st']I also remove index from the TODO list
99 with self.assertRaises(ValueError): 100 a_list.index('not in list') 101 102 103'insert', 104'pop', 105'remove', 106'reverse', 107'sort'
test_insert_item_at_position_in_a_list
RED: make it fail
I add a test for the next method
99 with self.assertRaises(ValueError):
100 a_list.index('not in list')
101
102 def test_insert(self):
103 a_list = [0, 1, 2, 'n']
104 self.assertIsNone(a_list.insert())
105
106
107'insert',
TypeError: insert expected 2 arguments, got 0
GREEN: make it pass
I add two values to the call
102 def test_insert(self):
103 a_list = [0, 1, 2, 'n']
104 self.assertIsNone(a_list.insert(0, 1))
the test passes. What did the insert method do to the list?
REFACTOR: make it better
I add an assertion to find out
104 self.assertIsNone(a_list.insert(0, 1)) 105 self.assertEqual(a_list, [0, 1, 2, 'n'])the terminal shows AssertionError
AssertionError: Lists differ: [1, 0, 1, 2, 'n'] != [0, 1, 2, 'n']there is an extra value at the beginning of the list
I add the new value to the expectation
105 self.assertEqual(a_list, [1, 0, 1, 2, 'n'])the test passes.
The insert method
I change the second input in the call
102 def test_insert(self): 103 a_list = [0, 1, 2, 'n'] 104 self.assertIsNone(a_list.insert(0, -1)) 105 self.assertEqual(a_list, [0, 1, 2, 'n'])the terminal shows AssertionError
AssertionError: Lists differ: [-1, 0, 1, 2, 'n'] != [1, 0, 1, 2, 'n']I change the expectation to match
105 self.assertEqual(a_list, [-1, 0, 1, 2, 'n'])the test is green again
I add another assertion to see what happens when I insert an item in the middle of the list
105 self.assertEqual(a_list, [-1, 0, 1, 2, 'n']) 106 self.assertIsNone(a_list.insert(3, 1.5))the terminal shows green
I add an assertion to see what it did to the list
106 self.assertIsNone(a_list.insert(3, 1.5)) 107 self.assertEqual(a_list, [-1, 0, 1, 2, 'n'])the terminal shows AssertionError
AssertionError: Lists differ: [-1, 0, 1, 1.5, 2, 'n'] != [-1, 0, 1, 2, 'n']I add the value to the expectation
107 self.assertEqual(a_list, [-1, 0, 1, 1.5, 2, 'n'])the test passes, it moved everything in the list from the index I gave and after, to the right
I change the name of the test
102 def test_insert_item_at_position_in_a_list(self): 103 a_list = [0, 1, 2, 'n'] 104 self.assertIsNone(a_list.insert(0, -1)) 105 self.assertEqual(a_list, [-1, 0, 1, 2, 'n']) 106 self.assertIsNone(a_list.insert(3, 1.5)) 107 self.assertEqual(a_list, [-1, 0, 1, 1.5, 2, 'n']) 108 109 110'insert',I remove insert from the TODO list
107 self.assertEqual(a_list, [-1, 0, 1, 1.5, 2, 'n']) 108 109 110'pop', 111'remove', 112'reverse', 113'sort'
test_pop_removes_and_returns_last_item_from_a_list
RED: make it fail
I add a new test
107 self.assertEqual(a_list, [-1, 0, 1, 1.5, 2, 'n'])
108
109 def test_pop(self):
110 a_list = [0, 1, 2, 'n']
111 self.assertIsNone(a_list.pop())
112
113
114'pop',
the terminal shows AssertionError
AssertionError: 'n' is not None
The pop method returns the last item in the list
GREEN: make it pass
I add the expectation
111 self.assertIsNone(a_list.pop(), 'n')
the terminal shows AssertionError
AssertionError: 'n' is not None : n
I change assertIsNone to assertEqual
109 def test_pop(self):
110 a_list = [0, 1, 2, 'n']
111 self.assertEqual(a_list.pop(), 'n')
the test passes
REFACTOR: make it better
I add another assertion to see what happened to the list
111 self.assertEqual(a_list.pop(), 'n') 112 self.assertEqual(a_list, [0, 1, 2, 'n'])the terminal shows AssertionError
AssertionError: Lists differ: [0, 1, 2] != [0, 1, 2, 'n']I change the values in the test to match
112 self.assertEqual(a_list, [0, 1, 2])the test passes
I add another assertion with the pop method
112 self.assertEqual(a_list, [0, 1, 2]) 113 self.assertEqual(a_list.pop(), 'n')the terminal shows AssertionError
AssertionError: 2 != 'n'I change the value in the expectation
113 self.assertEqual(a_list.pop(), 2)the test passes
I add another assertion to see what changed in the list
113 self.assertEqual(a_list.pop(), 2) 114 self.assertEqual(a_list, [0, 1, 2])the terminal shows AssertionError
AssertionError: Lists differ: [0, 1] != [0, 1, 2]I change the values in the test to match
114 self.assertEqual(a_list, [0, 1])the test passes
I change the name of the test
def test_pop_removes_and_returns_last_item_from_a_list(self): a_list = [0, 1, 2, 'n'] self.assertEqual(a_list.pop(), 'n') self.assertEqual(a_list, [0, 1, 2]) self.assertEqual(a_list.pop(), 2) self.assertEqual(a_list, [0, 1]) 'pop',I take out pop from the TODO list
114 self.assertEqual(a_list, [0, 1]) 115 116 117'remove', 118'reverse', 119'sort'
test_remove_first_time_item_is_in_a_list
RED: make it fail
time for the next method
114 self.assertEqual(a_list, [0, 1])
115
116 def test_remove(self):
117 a_list = [0, 1, 2, 'n']
118 self.assertIsNone(a_list.remove())
119
120
121'remove',
TypeError: list.remove() takes exactly one argument (0 given)
GREEN: make it pass
I add a value to the call
118 self.assertIsNone(a_list.remove(0))
the terminal shows green. The remove method returns None
REFACTOR: make it better
I add an assertion to see what it did to the list
118 self.assertIsNone(a_list.remove(0)) 119 self.assertEqual(a_list, [0, 1, 2, 'n'])the terminal shows AssertionError
AssertionError: Lists differ: [1, 2, 'n'] != [0, 1, 2, 'n']I change the expectation
119 self.assertEqual(a_list, [1, 2, 'n'])the test passes
I change the values in the list to see what happens when an item is in there more than one time
116 def test_remove(self): 117 a_list = [0, 1, 0, 2, 0, 'n'] 118 self.assertIsNone(a_list.remove(0)) 119 self.assertEqual(a_list, [1, 2, 'n'])the terminal shows AssertionError
AssertionError: Lists differ: [1, 0, 2, 0, 'n'] != [1, 2, 'n']I change the values to match
119 self.assertEqual(a_list, [1, 0, 2, 0, 'n'])the test passes. The remove method only removes the first time something shows up in the list
I want to see what happens when I try to remove something that is not in the list
119 self.assertEqual(a_list, [1, 0, 2, 0, 'n']) 120 self.assertIsNone(a_list.remove('not in list'))the terminal shows ValueError
ValueError: list.remove(x): x not in listI remove the things around the call then add assertRaises
119 self.assertEqual(a_list, [1, 0, 2, 0, 'n']) 120 121 with self.assertRaises(ValueError): 122 a_list.remove('not in list') 123 124 125'remove',the test passes
I change the name of the test
116 def test_remove_first_time_item_is_in_a_list(self): 117 a_list = [0, 1, 0, 2, 0, 'n'] 118 self.assertIsNone(a_list.remove(0)) 119 self.assertEqual(a_list, [1, 0, 2, 0, 'n']) 120 121 with self.assertRaises(ValueError): 122 a_list.remove('not in list') 123 124 125'remove'I take out remove from the TODO list
121 with self.assertRaises(ValueError): 122 a_list.remove('not in list') 123 124 125'reverse', 126'sort'
test_reverse_a_list
RED: make it fail
I add the next test
121 with self.assertRaises(ValueError): 122 a_list.remove('not in list') 123 124 def test_reverse(self): 125 a_list = [0, 1, 2, 'n'] 126 self.assertIsNone(a_list.reverse()) 127 128 129'reverse',the terminal shows green. The reverse method returns None.
I add an assertion to see what it did to the list
126 self.assertIsNone(a_list.reverse()) 127 self.assertEqual(a_list, [0, 1, 2, 'n'])the terminal shows AssertionError
AssertionError: Lists differ: ['n', 2, 1, 0] != [0, 1, 2, 'n']it reversed the order of the items in the list
GREEN: make it pass
I change the expectation
127 self.assertEqual(a_list, ['n', 2, 1, 0])
the test passes
REFACTOR: make it better
I change the name of the test
124 def test_reverse_a_list(self): 125 a_list = [0, 1, 2, 'n'] 126 self.assertIsNone(a_list.reverse()) 127 self.assertEqual(a_list, ['n', 2, 1, 0]) 128 129 130'reverse',I remove the name from the TODO list
127 self.assertEqual(a_list, ['n', 2, 1, 0]) 128 129 130'sort' 131 132 133# Exceptions seen
test_sort_a_list
RED: make it fail
I add a test for the last method in the TODO list
127 self.assertEqual(a_list, ['n', 2, 1, 0])
128
129 def test_sort(self):
130 a_list = [0, 1, 2, 'n']
131 self.assertIsNone(a_list.sort())
132
133
134'sort'
TypeError: '<' not supported between instances of 'str' and 'int'
I have to change 'n' to a number or change the other numbers to strings
GREEN: make it pass
I remove the things around the call then add assertRaises
129 def test_sort(self):
130 a_list = [0, 1, 2, 'n']
131 with self.assertRaises(TypeError):
132 a_list.sort()
the test passes
REFACTOR: make it better
I can use the list directly in the call
129 def test_sort(self): 130 a_list = [0, 1, 2, 'n'] 131 with self.assertRaises(TypeError): 132 [0, 1, 2, 'n'].sort()the test is still green
I remove the variable because it is no longer used
129 def test_sort(self): 130 with self.assertRaises(TypeError): 131 [0, 1, 2, 'n'].sort()still green
I add a new list where all the items are numbers and another assertion
129 def test_sort(self): 130 with self.assertRaises(TypeError): 131 [0, 1, 2, 'n'].sort() 132 133 a_list = [0, 1, 2, 3] 134 self.assertIsNone(a_list.sort())the terminal still shows green, sort returns None when called
I add another assertion to see what it did to the list
133 a_list = [0, 1, 2, 3] 134 self.assertIsNone(a_list.sort()) 135 self.assertEqual(a_list, [])the terminal shows AssertionError
AssertionError: Lists differ: [0, 1, 2, 3] != []the list stayed the same
I change the expectation to match
135 self.assertEqual(a_list, [0, 1, 2, 3])the test passes. The name of the method is sort and I gave it a list that is sorted
I change the list to see what happens when it is NOT sorted
133 a_list = [0, 1, -1, 2, -2, 3, -3] 134 self.assertIsNone(a_list.sort()) 135 self.assertEqual(a_list, [0, 1, 2, 3])the terminal shows AssertionError
AssertionError: Lists differ: [-3, -2, -1, 0, 1, 2, 3] != [0, 1, 2, 3]the sort method changed the order of the list from smallest to biggest number
I change the values to match
135 self.assertEqual(a_list, [-3, -2, -1, 0, 1, 2, 3])the test passes
I change the name of the test
129 def test_sort_a_list(self): 130 with self.assertRaises(TypeError): 131 [0, 1, 2, 'n'].sort() 132 133 a_list = [0, 1, -1, 2, -2, 3, -3] 134 self.assertIsNone(a_list.sort()) 135 self.assertEqual(a_list, [-3, -2, -1, 0, 1, 2, 3]) 136 137 138'sort'I remove sort from the TODO list
135 self.assertEqual(a_list, [-3, -2, -1, 0, 1, 2, 3]) 136 137 138# Exceptions seen 139# AssertionError 140# TypeError 141# ValueError
test_getting_items_of_a_list
When I want an item that is in a list, I can give its index in square brackets([]) to get it
RED: make it fail
I add a failing test
135 self.assertEqual(a_list, [-3, -2, -1, 0, 1, 2, 3])
136
137 def test_getting_items_of_a_list(self):
138 a_list = ['1st', '2nd', '3rd', '...last']
139 self.assertEqual(a_list[0], '')
140
141
142# Exceptions seen
the terminal shows AssertionError
AssertionError: '1st' != ''
the first item has an index of 0
GREEN: make it pass
I change the value in the test
139 self.assertEqual(a_list[0], '1st')
the test passes
REFACTOR: make it better
this is the opposite of the index method which takes in the item and returns its position, in this case I provide the index (position) and it returns the item, which means I can write this
139 self.assertEqual(a_list[0], '1st') 140 self.assertEqual(a_list[a_list.index('1st')], '')the terminal shows AssertionError
AssertionError: '1st' != ''I change the value to match
140 self.assertEqual(a_list[a_list.index('1st')], '1st')the test passes because
a_list.index('1st')returns0a_list[0]returns1st
I can also use negative numbers. The last item has an index of
-1and the first item has an index of negative the length of the list140 self.assertEqual(a_list[a_list.index('1st')], '1st') 141 self.assertEqual(a_list[-4], '')the terminal shows AssertionError
AssertionError: '1st' != ''I change the value to match
141 self.assertEqual(a_list[-4], '1st')the test passes
I add another assertion
141 self.assertEqual(a_list[-4], '1st') 142 self.assertEqual(a_list[2], '')the terminal shows AssertionError
AssertionError: '3rd' != ''I change the expectation to match
142 self.assertEqual(a_list[2], '3rd')the terminal shows green again
I add another assertion
142 self.assertEqual(a_list[2], '3rd') 143 self.assertEqual(a_list[-2], '')the terminal shows AssertionError
AssertionError: '3rd' != ''I change the expectation
143 self.assertEqual(a_list[-2], '3rd')the test passes
I add another line
143 self.assertEqual(a_list[-2], '3rd') 144 self.assertEqual(a_list[1], '')the terminal shows AssertionError
AssertionError: '2nd' != ''I change the expectation
144 self.assertEqual(a_list[1], '2nd')I add another
144 self.assertEqual(a_list[1], '2nd') 145 self.assertEqual(a_list[-3], '')the terminal shows AssertionError
AssertionError: '2nd' != ''I change the value
145 self.assertEqual(a_list[-3], '2nd')the test passes
I add another assertion
145 self.assertEqual(a_list[-3], '2nd') 146 self.assertEqual(a_list[3], '')the terminal shows AssertionError
AssertionError: '...last' != ''I change the value
146 self.assertEqual(a_list[3], '...last')the test passes
I add another assertion
146 self.assertEqual(a_list[3], '...last') 147 self.assertEqual(a_list[-1], '')the terminal shows AssertionError
AssertionError: '...last' != ''I make the values match
137 def test_getting_items_of_a_list(self): 138 a_list = ['1st', '2nd', '3rd', '...last'] 139 self.assertEqual(a_list[0], '1st') 140 self.assertEqual(a_list[a_list.index('1st')], '1st') 141 self.assertEqual(a_list[-4], '1st') 142 self.assertEqual(a_list[2], '3rd') 143 self.assertEqual(a_list[-2], '3rd') 144 self.assertEqual(a_list[1], '2nd') 145 self.assertEqual(a_list[-3], '2nd') 146 self.assertEqual(a_list[3], '...last') 147 self.assertEqual(a_list[-1], '...last') 148 149 150# Exceptions seenthe test passes
test_setting_items_in_a_list
I can also use the index of an item in a list to change the item
RED: make it fail
I add an assertion
147 self.assertEqual(a_list[-1], '...last')
148
149 def test_setting_items_in_a_list(self):
150 a_list = ['1st', '2nd', '3rd', '...last']
151 a_list[-1] = '4th'
152 self.assertEqual(a_list, ['1st', '2nd', '3rd', '...last'])
153
154
155# Exceptions seen
the terminal shows AssertionError
AssertionError: Lists differ: ['1st', '2nd', '3rd', '4th'] != ['1st', '2nd', '3rd', '...last']
I can use the index of an item to change its value in a list, the way I point a name to a value when I make a variable
GREEN: make it pass
I change the expectation to match the values in the terminal
152 self.assertEqual(a_list, ['1st', '2nd', '3rd', '4th'])
the test passes. I know how to change the value of an item in a list
test_looking_at_parts_of_a_list_aka_slicing
I can use indices to look at parts of a list, this is called slicing
RED: make it fail
I add another test
152 self.assertEqual(a_list, ['1st', '2nd', '3rd', '4th'])
153
154 def test_looking_at_parts_of_a_list(self):
155 a_list = ['a', 'b', 'c', 'd']
156 self.assertEqual(a_list[0:2], [])
157
158
159 # Exceptions seen
the terminal shows AssertionError
AssertionError: Lists differ: ['a', 'b'] != []
GREEN: make it pass
I change the values to match
156 self.assertEqual(a_list[0:2], ['a', 'b'])
the test passes.
I give two values in square brackets([]), separated by a :, the first value is the index of the item I want to start from, and the second value is the index of the item I want to stop at plus 1
REFACTOR: make it better
I can skip the first number when the starting index is
0155 self.assertEqual(a_list[0:2], ['a', 'b']) 156 self.assertEqual(a_list[:2], [])the terminal shows AssertionError
AssertionError: Lists differ: ['a', 'b'] != []I change the values to match
157 self.assertEqual(a_list[:2], ['a', 'b'])the terminal shows green again.
[0:2]and[:2]are the sameI add another assertion
157 self.assertEqual(a_list[:2], ['a', 'b']) 158 self.assertEqual(a_list[1:4], [])the terminal shows AssertionError
AssertionError: Lists differ: ['b', 'c', 'd'] != []I add the missing values
158 self.assertEqual(a_list[1:4], ['b', 'c', 'd'])the test passes
I can skip the second number when it is bigger than or the same as the number of things in the list
158 self.assertEqual(a_list[1:4], ['b', 'c', 'd']) 159 self.assertEqual(a_list[1:], [])the terminal shows AssertionError
AssertionError: Lists differ: ['b', 'c', 'd'] != []I add the missing values
159 self.assertEqual(a_list[1:], ['b', 'c', 'd'])the test passes.
[1:4]and[1:]are the same because this list has 4 thingsI add another line
159 self.assertEqual(a_list[1:], ['b', 'c', 'd']) 160 self.assertEqual(a_list[0:3], [])the terminal shows AssertionError
AssertionError: Lists differ: ['a', 'b', 'c'] != []I change the expectation
160 self.assertEqual(a_list[0:3], ['a', 'b', 'c'])the test passes
I add another
160 self.assertEqual(a_list[0:3], ['a', 'b', 'c']) 161 self.assertEqual(a_list[1:3], [])the terminal shows AssertionError
AssertionError: Lists differ: ['b', 'c'] != []I change the expectation
161 self.assertEqual(a_list[1:3], ['b', 'c'])the test is green again
I can also skip both numbers
161 self.assertEqual(a_list[1:3], ['b', 'c']) 162 self.assertEqual(a_list[:], [])the terminal shows AssertionError
AssertionError: Lists differ: ['a', 'b', 'c', 'd'] != []I get the entire list back or a copy
162 self.assertEqual(a_list[:], a_list.copy())the test is green again
This is also called slicing, I change the name of the test
154 def test_looking_at_parts_of_a_list_aka_slicing(self): 155 a_list = ['a', 'b', 'c', 'd'] 156 self.assertEqual(a_list[0:2], ['a', 'b']) 157 self.assertEqual(a_list[:2], ['a', 'b']) 158 self.assertEqual(a_list[1:4], ['b', 'c', 'd']) 159 self.assertEqual(a_list[1:], ['b', 'c', 'd']) 160 self.assertEqual(a_list[0:3], ['a', 'b', 'c']) 161 self.assertEqual(a_list[1:3], ['b', 'c']) 162 self.assertEqual(a_list[:], a_list.copy()) 163 164 165# Exceptions seen
test_index_error
IndexError is raised when I try to get an item from a list but use a number that points to something that is NOT in it. When I see this Exception I know the underlying data structure is a list
RED: make it fail
I add a failing test
162 self.assertEqual(a_list[:], a_list.copy())
163
164 def test_index_error(self):
165 a_list = ['a', 'b', 'c', 'd']
166 a_list[4]
167
168
169# Exceptions seen
the terminal shows IndexError
IndexError: list index out of range
when I use an index that is greater than or the same as the number of things in the list, I am pointing to something that is NOT in the list
GREEN: make it pass
I add assertRaises
164def test_index_error(self):
165 a_list = ['a', 'b', 'c', 'd']
166
167 with self.assertRaises(IndexError):
168 a_list[4]
the test passes
REFACTOR: make it better
I add IndexError to the list of Exceptions seen
171# Exceptions seen 172# AssertionError 173# TypeError 174# ValueError 175# IndexErrorI get IndexError when I use a number that is smaller than the number of things in the list as a negative number
167 with self.assertRaises(IndexError): 168 a_list[4] 169 a_list[-5]the terminal shows IndexError
IndexError: list index out of rangeI add the assertRaises method
167 with self.assertRaises(IndexError): 168 a_list[4] 169 with self.assertRaises(IndexError): 170 a_list[-5] 171 172 173# Exceptions seenthe test passes
IndexError is also raised when I call the pop method with an empty list
167 with self.assertRaises(IndexError): 168 a_list[4] 169 with self.assertRaises(IndexError): 170 a_list[-5] 171 [].pop()the terminal shows IndexError
IndexError: pop from empty listI add assertRaises
169 with self.assertRaises(IndexError): 170 a_list[-5] 171 with self.assertRaises(IndexError): 172 [].pop()the terminal shows green
I cannot remove the last item from a list that has no items, this is the same as trying to get an item from a list that has no items
171 with self.assertRaises(IndexError): 172 [].pop() 173 [][-1]the terminal shows IndexError
IndexError: list index out of rangeI add assertRaises
171 with self.assertRaises(IndexError): 172 [].pop() 173 with self.assertRaises(IndexError): 174 [][-1]the test passes. Any index given to an empty list raises IndexError
close the project
I close
test_lists.pyin the editorI click in the terminal and exit the tests with ctrl+c on the keyboard, the terminal shows
.../pumping_pythonI am back in the
pumping_pythondirectory
Note
on Windows without Windows Subsystem for Linux
the terminal shows
(.venv) ...\pumping_python\listsI deactivate the virtual environment
deactivatethe terminal goes back to the command line,
(.venv)is no longer on the left side...\pumping_python\listsI change directory to the parent of
listscd ..the terminal shows
...\pumping_pythonI am back in the
pumping_pythondirectory
test_calculator_sends_message_when_input_is_a_list
I want to see what happens when I send a list as input to the calculator program, will it send a message or raise TypeError?
open the project
I change directory to the
calculatorfoldercd calculatorthe terminal shows I am in the
calculatorfolder.../pumping_python/calculatorI activate the virtual environment
source .venv/bin/activateAttention
on Windows without Windows Subsystem for Linux use
.venv/bin/activate.ps1instead ofsource .venv/bin/activate.venv/scripts/activate.ps1the terminal shows
(.venv) .../pumping_python/calculatorI use
pytest-watchto run the testspytest-watchthe terminal shows
rootdir: .../pumping_python/calculator collected 5 items tests/test_calculator.py .... [100%] ============================ 5 passed in X.YZs =============================I hold ctrl on the keyboard and click on
tests/test_calculator.pyto open it in the editor
RED: make it fail
I add a test to see what happens when I send a list as input
88 self.assertEqual(
89 src.calculator.subtract('1', '1'),
90 error_message
91 )
92
93 def test_calculator_sends_message_when_input_is_a_list(self):
94 a_list = [0, 1, 2, 3]
95
96 self.assertEqual(
97 src.calculator.add(a_list, 0),
98 'BOOM!!!'
99 )
100
101
102# Exceptions seen
the terminal shows AssertionError
AssertionError: 'Excuse me?! I only work with numbers. Please try again...' != 'BOOM!!!'
green: make it pass
I change the expectation to match
96 self.assertEqual(
97 src.calculator.add(a_list, 0),
98 'Excuse me?! I only work with numbers. Please try again...'
99 )
the test passes
refactor: make it better
I add another assertion for the next function
96 self.assertEqual( 97 src.calculator.add(a_list, 0), 98 'Excuse me?! I only work with numbers. Please try again...' 99 ) 100 self.assertEqual( 101 src.calculator.divide(a_list, 1), 102 'BAP!!!' 103 )the terminal shows AssertionError
AssertionError: 'Excuse me?! I only work with numbers. Please try again...' != 'BAP!!!'I change the expectation
101 self.assertEqual( 102 src.calculator.divide(a_list, 1), 103 'Excuse me?! I only work with numbers. Please try again...' 104 )the test passes. Wait a minute! I just wrote the same thing twice, and I did it 8 times before in test_calculator_sends_message_when_input_is_not_a_number and 2 times in the
only_takes_numbersfunction. Never againI add a variable to remove the repetition
93 def test_calculator_sends_message_when_input_is_a_list(self): 94 a_list = [0, 1, 2, 3] 95 error_message = 'Excuse me?! I only work with numbers. Please try again...' 96 97 self.assertEqual( 98 src.calculator.add(a_list, 0), 99 error_message 100 ) 101 self.assertEqual( 102 src.calculator.divide(a_list, 1), 103 error_message 104 )the test is still green
how to multiply a list
I add an assertion for the multiply function
101 self.assertEqual( 102 src.calculator.divide(a_list, 1), 103 error_message 104 ) 105 self.assertEqual( 106 src.calculator.multiply(a_list, 2), 107 'BOOM!!!' 108 )the terminal shows AssertionError
AssertionError: [0, 1, 2, 3, 0, 1, 2, 3] != 'BOOM!!!'I change the expectation of the test to the error message
105 self.assertEqual( 106 src.calculator.multiply(a_list, 2), 107 error_message 108 )the terminal shows AssertionError
AssertionError: [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] != 'Excuse me?! I only work with numbers. Please try again...'I open
calculator.pyin the editorI add an if statement to the
only_takes_numbersfunction incalculator.py1def only_takes_numbers(function): 2 def wrapper(first_input, second_input): 3 error_message = 'Excuse me?! I only work with numbers. Please try again...' 4 5 if isinstance(first_input, str) or isinstance(second_input, str): 6 return error_message 7 if isinstance(first_input, list) or isinstance(second_input, list): 8 return error_message 9 10 try: 11 return function(first_input, second_input) 12 except TypeError: 13 return error_message 14 return wrapperthe test passes. The
only_takes_numbersfunction looks ugly now, there has to be a better wayI add an assertion for the subtract function to
test_calculator.py105 self.assertEqual( 106 src.calculator.multiply(a_list, 2), 107 error_message 108 ) 109 self.assertEqual( 110 src.calculator.subtract(a_list, 3), 111 'BOOM!!!' 112 )the terminal shows AssertionError
AssertionError: 'Excuse me?! I only work with numbers. Please try again...' != 'BOOM!!!'I change the expectation to match
109 self.assertEqual( 110 src.calculator.subtract(a_list, 3), 111 error_message 112 )the test passes
I remove the name of the test to move the new assertions to
test_calculator_sends_message_when_input_is_not_a_number88 self.assertEqual( 89 src.calculator.subtract('1', '1'), 90 error_message 91 ) 92 93 a_list = [0, 1, 2, 3] 94 error_message = 'Excuse me?! I only work with numbers. Please try again...' 95 96 self.assertEqual( 97 src.calculator.add(a_list, 0), 98 error_message 99 ) 100 self.assertEqual( 101 src.calculator.divide(a_list, 1), 102 error_message 103 ) 104 self.assertEqual( 105 src.calculator.multiply(a_list, 2), 106 error_message 107 ) 108 self.assertEqual( 109 src.calculator.subtract(a_list, 3), 110 error_message 111 ) 112 113 114# Exceptions seenthe tests are still green
I remove the duplication of the
error_messagevariable88 self.assertEqual( 89 src.calculator.subtract('1', '1'), 90 error_message 91 ) 92 93 a_list = [0, 1, 2, 3] 94 95 self.assertEqual( 96 src.calculator.add(a_list, 0), 97 error_message 98 )still green. This test looks long, there has to be a better way to test the calculator with inputs that are NOT numbers
how to test if something is an instance of more than one type
The isinstance function can take a tuple as the second input, which allows me to check if the first input is an instance of any of the objects in the tuple
I add a variable to the
only_takes_numbersfunction incalculator.py1def only_takes_numbers(function): 2 def wrapper(first_input, second_input): 3 bad_types = (str, list) 4 error_message = 'Excuse me?! I only work with numbers. Please try again...' 5 6 # if isinstance(first_input, str) or isinstance(second_input, str): 7 # return error_message 8 # if isinstance(first_input, list) or isinstance(second_input, list): 9 # return error_message 10 11 if isinstance(first_input, bad_types) or isinstance(second_input, bad_types): 12 return error_message 13 14 try: 15 return function(first_input, second_input) 16 except TypeError: 17 return error_message 18 return wrapperthe tests are still green
I remove the comments
1def only_takes_numbers(function): 2 def wrapper(first_input, second_input): 3 bad_types = (str, list) 4 error_message = 'Excuse me?! I only work with numbers. Please try again...' 5 6 if isinstance(first_input, bad_types) or isinstance(second_input, bad_types): 7 return error_message 8 9 try: 10 return function(first_input, second_input) 11 except TypeError: 12 return error_message 13 return wrapperstill green
I can use Logical Negation (NOT) to make the if statement allow only the types of numbers (integers and floats) that I want the calculator to handle
1def only_takes_numbers(function): 2 def wrapper(first_input, second_input): 3 good_types = (int, float) 4 bad_types = (str, list) 5 error_message = 'Excuse me?! I only work with numbers. Please try again...' 6 7 # if isinstance(first_input, bad_types) or isinstance(second_input, bad_types): 8 # return error_message 9 10 if not isinstance(first_input, good_types) or not isinstance(second_input, good_types): 11 return error_message 12 else: 13 try: 14 return function(first_input, second_input) 15 except TypeError: 16 return error_message 17 return wrapperthe tests are still green
I remove the comments and the
bad_typesvariable1def only_takes_numbers(function): 2 def wrapper(first_input, second_input): 3 good_types = (int, float) 4 error_message = 'Excuse me?! I only work with numbers. Please try again...' 5 6 if not isinstance(first_input, good_types) or not isinstance(second_input, good_types): 7 return error_message 8 else: 9 try: 10 return function(first_input, second_input) 11 except TypeError: 12 return error_message 13 return wrapperthe tests are still passing
“not” happens twice in the if statement, I write the line in terms of it
6 # if not isinstance(first_input, good_types) or not isinstance(second_input, good_types): 7 if (not isinstance(first_input, good_types)) (not and) (not isinstance(second_input, good_types)): 8 return error_messagethe terminal shows SyntaxError
SyntaxError: invalid syntaxI add SyntaxError to the list of Exceptions in
test_calculator.py113# Exceptions seen 114# AssertionError 115# NameError 116# AttributeError 117# TypeError 118# ZeroDivisionError 119# SyntaxErrorI fix the if statement<if statement> in
calculator.py6 # if not isinstance(first_input, good_types) or not isinstance(second_input, good_types): 7 # if (not isinstance(first_input, good_types)) (not and) ((not isinstance(second_input, good_types))): 8 if not (isinstance(first_input, good_types) and isinstance(second_input, good_types)): 9 return error_messagethe test is green again
I remove the comments
1def only_takes_numbers(function): 2 def wrapper(first_input, second_input): 3 good_types = (int, float) 4 error_message = 'Excuse me?! I only work with numbers. Please try again...' 5 6 if not (isinstance(first_input, good_types) and isinstance(second_input, good_types)): 7 return error_message 8 else: 9 try: 10 return function(first_input, second_input) 11 except TypeError: 12 return error_message 13 return wrapperall the tests are still passing. I wonder if there is a way to write this function with only one return statement for the error message
test_calculator_w_list_items
I can use a list to test the calculator functions as long as its items are numbers
red: make it fail
I add a new test to use the index of the items in the list to test the calculator
107 self.assertEqual(
108 src.calculator.subtract(a_list, 3),
109 error_message
110 )
111
112 def test_calculator_w_list_items(self):
113 a_list = [self.random_first_number, self.random_second_number]
114
115 self.assertEqual(
116 src.calculator.add(a_list[0], a_list[1]),
117 self.random_first_number-self.random_second_number
118 )
119
120
121 # Exceptions seen
the terminal shows AssertionError
AssertionError: ABC.DEFGHIJKLMNOPQ != RST.UVWXYZABCDEFG
green: make it pass
I change the expectation to the right calculation
115 self.assertEqual(
116 src.calculator.add(a_list[0], a_list[1]),
117 self.random_first_number+self.random_second_number
118 )
the test passes
refactor: make it better
I add an assertion for the divide function
115 self.assertEqual( 116 src.calculator.add(a_list[0], a_list[1]), 117 self.random_first_number+self.random_second_number 118 ) 119 self.assertEqual( 120 src.calculator.divide(a_list[-2], a_list[-1]), 121 self.random_first_number*self.random_second_number 122 )the terminal shows AssertionError
AssertionError: D.EFGHIJKLMNOPQRST != UVWXY.ZABCDEFGHIJI change the calculation
119 self.assertEqual( 120 src.calculator.divide(a_list[-2], a_list[-1]), 121 self.random_first_number/self.random_second_number 122 )the test passes
I add another assertion
119 self.assertEqual( 120 src.calculator.divide(a_list[-2], a_list[-1]), 121 self.random_first_number/self.random_second_number 122 ) 123 self.assertEqual( 124 src.calculator.multiply(a_list[1], a_list[-1]), 125 self.random_first_number*self.random_second_number 126 )the terminal shows AssertionError
AssertionError: EFGHIJ.KLMNOPQRSTU != VWXYZ.ABCDEFGHIJKLI change the expectation
123 self.assertEqual( 124 src.calculator.multiply(a_list[1], a_list[-1]), 125 self.random_second_number*self.random_second_number 126 )the test passes
I add an assertion for the subtract function
123 self.assertEqual( 124 src.calculator.multiply(a_list[1], a_list[-1]), 125 self.random_second_number*self.random_second_number 126 ) 127 self.assertEqual( 128 src.calculator.subtract(a_list[-2], a_list[0]), 129 self.random_first_number-self.random_second_number 130 )the terminal shows AssertionError
AssertionError: 0.0 != FGH.IJKLMNOPQRSTUI change the expectation to match
127 self.assertEqual( 128 src.calculator.subtract(a_list[-2], a_list[0]), 129 self.random_first_number-self.random_first_number 130 )the test passes
Python allows me use a star expression like I did in test_functions_w_unknown_arguments. I add an assertion with it
127 self.assertEqual( 128 src.calculator.subtract(a_list[-2], a_list[0]), 129 self.random_first_number-self.random_first_number 130 ) 131 self.assertEqual( 132 src.calculator.add(*a_list), 133 self.random_first_number-self.random_second_number 134 )the terminal shows AssertionError
AssertionError: GHI.JKLMNOPQRSTUVW != XYZ.ABCDEFGHIJKLMNI change the expectation
131 self.assertEqual( 132 src.calculator.add(*a_list), 133 self.random_first_number+self.random_second_number 134 )the test passes
I add another assertion
131 self.assertEqual( 132 src.calculator.add(*a_list), 133 self.random_first_number+self.random_second_number 134 ) 135 self.assertEqual( 136 src.calculator.divide(*a_list), 137 self.random_first_number*self.random_second_number 138 )the terminal shows AssertionError
AssertionError: H.IJKLMNOPQRSTUVWX != YZABCD.EFGHIJKLMNOI change the calculation
135 self.assertEqual( 136 src.calculator.divide(*a_list), 137 self.random_first_number/self.random_second_number 138 )the test passes
I add an assertion for the multiply function
135 self.assertEqual( 136 src.calculator.divide(*a_list), 137 self.random_first_number/self.random_second_number 138 ) 139 self.assertEqual( 140 src.calculator.multiply(*a_list), 141 self.random_first_number/self.random_second_number 142 )the terminal shows AssertionError
AssertionError: IJKLMN.OPQRSTUVWX != Y.ZABCDEFGHIJKLMNOPI change the calculation
139 self.assertEqual( 140 src.calculator.multiply(*a_list), 141 self.random_first_number*self.random_second_number 142 )the test passes
I add the next assertion
139 self.assertEqual( 140 src.calculator.multiply(*a_list), 141 self.random_first_number*self.random_second_number 142 ) 143 self.assertEqual( 144 src.calculator.subtract(*a_list), 145 self.random_first_number+self.random_second_number 146 )the terminal shows AssertionError
AssertionError: JKL.MNOPQRSTUVWXYZ != ABC.DEFGHIJKLMNOPI change the expectation
143 self.assertEqual( 144 src.calculator.subtract(*a_list), 145 self.random_first_number-self.random_second_number 146 )the test passes
test_calculator_raises_type_error_when_given_more_than_two_inputs
It is important to note that the star expression always gives the items from the list in order, and I cannot use a list that has more than 2 items with these calculator functions since they only take 2 inputs
red: make it fail
I add a new test to show the problem when I have more than 2 inputs and use a star expression
143 self.assertEqual(
144 src.calculator.subtract(*a_list),
145 self.random_first_number-self.random_second_number
146 )
147
148 def test_calculator_raises_type_error_when_given_more_than_two_inputs(self):
149 another_list = [0, 1, 2]
150
151 src.calculator.add(*another_list)
152
153
154# Exceptions seen
TypeError: only_takes_numbers.<locals>.wrapper() takes 2 positional arguments but 3 were given
green: make it pass
I add the assertRaises method to handle the Exception
148 def test_calculator_raises_type_error_when_given_more_than_two_inputs(self):
149 another_list = [0, 1, 2]
150
151 with self.assertRaises(TypeError):
152 src.calculator.add(*another_list)
the test passes
refactor: make it better
I add a failing line for division with the new list
151 with self.assertRaises(TypeError): 152 src.calculator.add(*another_list) 153 src.calculator.divide(*another_list)TypeError: only_takes_numbers.<locals>.wrapper() takes 2 positional arguments but 3 were givenI add assertRaises
151 with self.assertRaises(TypeError): 152 src.calculator.add(*another_list) 153 with self.assertRaises(TypeError): 154 src.calculator.divide(*another_list) 155 156the test passesI add a line for multiplication
153 with self.assertRaises(TypeError): 154 src.calculator.divide(*another_list) 155 src.calculator.multiply(*another_list)TypeError: only_takes_numbers.<locals>.wrapper() takes 2 positional arguments but 3 were givenI add assertRaises
153 with self.assertRaises(TypeError): 154 src.calculator.divide(*another_list) 155 with self.assertRaises(TypeError): 156 src.calculator.multiply(*another_list)the test passes
I add the last line
155 with self.assertRaises(TypeError): 156 src.calculator.multiply(*another_list) 157 src.calculator.subtract(*another_list)TypeError: only_takes_numbers.<locals>.wrapper() takes 2 positional arguments but 3 were givenI handle the Exception
148 def test_calculator_raises_type_error_when_given_more_than_two_inputs(self): 149 another_list = [0, 1, 2] 150 151 with self.assertRaises(TypeError): 152 src.calculator.add(*another_list) 153 with self.assertRaises(TypeError): 154 src.calculator.divide(*another_list) 155 with self.assertRaises(TypeError): 156 src.calculator.multiply(*another_list) 157 with self.assertRaises(TypeError): 158 src.calculator.subtract(*another_list)the test passes
close the project
I close
test_calculator.pyandcalculator.pyin the editorI click in the terminal and exit the tests with ctrl+c on the keyboard, the terminal shows
(.venv) .../pumping_python/calculatorI deactivate the virtual environment
deactivatethe terminal goes back to the command line,
(.venv)is no longer on the left side.../pumping_python/calculatorI change directory to the parent of
calculatorcd ..the terminal shows
.../pumping_pythonI am back in the
pumping_pythondirectory
review
I ran tests to show that I can make a list with the constructor list() or square brackets ([]), then I ran the following tests for the methods of lists from append to sort
I also added tests for
How many questions can you answer after going through this chapter?
code from the chapter
what is next?
you know
Would you like to test list comprehensions? They are a quick way to make lists