how to make a calculator 5
I want to practice using lists with the calculator project
preview
These are the tests I have by the end of the chapter
open the project
I change directory to the
calculatorfoldercd calculatorthe terminal shows I am in the
calculatorfolder.../pumping_python/calculatorI use
pytest-watcherto run the testsuv run pytest-watcher . --nowthe terminal shows
rootdir: .../pumping_python/calculator configfile: pyproject.toml collected 5 items tests/test_calculator.py ..... [100%] ======================== 5 passed in X.YZs =========================I hold ctrl on the keyboard, then click on
tests/test_calculator.pyto open it in the editor
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?
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: 'brmph?! Numbers only. Try again...' != 'BOOM!!!'
GREEN: make it pass
I change the expectation to match
96 self.assertEqual(
97 src.calculator.add(a_list, 0),
98 'brmph?! Numbers only. Try again...'
99 )
the test passes
REFACTOR: make it better
I add another assertion for the next function
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 'brmph?! Numbers only. Try again...' 99 ) 100 self.assertEqual( 101 src.calculator.divide(a_list, 1), 102 'BAP!!!' 103 ) 104 105 106# Exceptions seenthe terminal shows AssertionError
AssertionError: 'brmph?! Numbers only. Try again...' != 'BAP!!!'I change the expectation
101 self.assertEqual( 102 src.calculator.divide(a_list, 1), 103 'brmph?! Numbers only. 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
numbers_onlyfunction. Never againI add a variable
93 def test_calculator_sends_message_when_input_is_a_list(self): 94 a_list = [0, 1, 2, 3] 95 error_message = 'brmph?! Numbers only. Try again...' 96 97 self.assertEqual( 98 src.calculator.add(a_list, 0), 99 'brmph?! Numbers only. Try again...' 100 )I use the 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 = 'brmph?! Numbers only. 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 ) 109 110 111# Exceptions seenthe 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] != 'brmph?! Numbers only. Try again...'I open
calculator.pyin the editorI add an if statement to the multiply function in
calculator.py19@numbers_only 20def multiply(first_input, second_input): 21 if ( 22 isinstance(first_input, list) 23 or 24 isinstance(second_input, list) 25 ): 26 return 'brmph?! Numbers only. Try again...' 27 return first_input * second_inputthe test passes
I add an assertion for the subtract function in test_calculator_sends_message_when_input_is_a_list 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 ) 113 114 115# Exceptions seenthe terminal shows AssertionError
AssertionError: 'brmph?! Numbers only. Try again...' != 'BOOM!!!'I change the expectation to match
93 def test_calculator_sends_message_when_input_is_a_list(self): 94 a_list = [0, 1, 2, 3] 95 error_message = 'brmph?! Numbers only. 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 ) 105 self.assertEqual( 106 src.calculator.multiply(a_list, 2), 107 error_message 108 ) 109 self.assertEqual( 110 src.calculator.subtract(a_list, 3), 111 error_message 112 ) 113 114 115# Exceptions seenthe test passes
I remove the name of the test to move the new assertions to test_calculator_sends_message_when_input_is_not_a_number
84 self.assertEqual( 85 src.calculator.multiply('1', '1'), 86 error_message 87 ) 88 self.assertEqual( 89 src.calculator.subtract('1', '1'), 90 error_message 91 ) 92 93 a_list = [0, 1, 2, 3] 94 error_message = 'brmph?! Numbers only. 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 )the tests are still green
I remove the repetition of the
error_messagevariable57 def test_calculator_sends_message_when_input_is_not_a_number(self): 58 error_message = 'brmph?! Numbers only. Try again...' 59 60 self.assertEqual( 61 src.calculator.add(None, None), 62 error_message 63 ) 64 self.assertEqual( 65 src.calculator.divide(None, None), 66 error_message 67 ) 68 self.assertEqual( 69 src.calculator.multiply(None, None), 70 error_message 71 ) 72 self.assertEqual( 73 src.calculator.subtract(None, None), 74 error_message 75 ) 76 self.assertEqual( 77 src.calculator.add('1', '1'), 78 error_message 79 ) 80 self.assertEqual( 81 src.calculator.divide('1', '1'), 82 error_message 83 ) 84 self.assertEqual( 85 src.calculator.multiply('1', '1'), 86 error_message 87 ) 88 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 ) 99 self.assertEqual( 100 src.calculator.divide(a_list, 1), 101 error_message 102 ) 103 self.assertEqual( 104 src.calculator.multiply(a_list, 2), 105 error_message 106 ) 107 self.assertEqual( 108 src.calculator.subtract(a_list, 3), 109 error_message 110 ) 111 112 113# Exceptions seenstill green. test_calculator_sends_message_when_input_is_not_a_number is getting long, there has to be a better way to test the calculator with inputs that are NOT numbers
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 two_numbers = [
114 self.random_first_number,
115 self.random_second_number
116 ]
117
118 self.assertEqual(
119 src.calculator.add(
120 two_numbers[0],
121 two_numbers[1]
122 ),
123 self.random_first_number-self.random_second_number
124 )
125
126
127 # Exceptions seen
the terminal shows AssertionError
AssertionError: ABC.DEFGHIJKLMNOPQ != RST.UVWXYZABCDEFG
GREEN: make it pass
I change the expectation to the right calculation
118 self.assertEqual(
119 src.calculator.add(
120 two_numbers[0],
121 two_numbers[1]
122 ),
123 self.random_first_number+self.random_second_number
124 )
the test passes. two_numbers is a list with two items - self.random_first_number and self.random_second_number, this means
two_numbers[0]isself.random_first_numbertwo_numbers[1]isself.random_second_number
REFACTOR: make it better
I add an assertion for the divide function
118 self.assertEqual( 119 src.calculator.add( 120 two_numbers[0], 121 two_numbers[1] 122 ), 123 self.random_first_number+self.random_second_number 124 ) 125 self.assertEqual( 126 src.calculator.divide( 127 two_numbers[-2], 128 two_numbers[-1] 129 ), 130 self.random_first_number*self.random_second_number 131 )the terminal shows AssertionError
AssertionError: D.EFGHIJKLMNOPQRST != UVWXY.ZABCDEFGHIJI change the calculation to division
125 self.assertEqual( 126 src.calculator.divide( 127 two_numbers[-2], 128 two_numbers[-1] 129 ), 130 self.random_first_number/self.random_second_number 131 )the test passes.
two_numbersis a list with two items -self.random_first_numberandself.random_second_number, this means
two_numbers[-2]isself.random_first_numbertwo_numbers[-1]isself.random_second_number
I add an assertion for multiplication
125 self.assertEqual( 126 src.calculator.divide( 127 two_numbers[-2], 128 two_numbers[-1] 129 ), 130 self.random_first_number/self.random_second_number 131 ) 132 self.assertEqual( 133 src.calculator.multiply( 134 two_numbers[1], 135 two_numbers[-1] 136 ), 137 self.random_first_number*self.random_second_number 138 )the terminal shows AssertionError
AssertionError: EFGHIJ.KLMNOPQRSTU != VWXYZ.ABCDEFGHIJKLI change the expectation
132 self.assertEqual( 133 src.calculator.multiply( 134 two_numbers[1], 135 two_numbers[-1] 136 ), 137 self.random_second_number*self.random_second_number 138 )the test passes.
two_numbersis a list with two items -self.random_first_numberandself.random_second_number, this means
two_numbers[1]isself.random_second_numbertwo_numbers[-1]isself.random_second_number
I add an assertion for the subtract function
132 self.assertEqual( 133 src.calculator.multiply( 134 two_numbers[1], 135 two_numbers[-1] 136 ), 137 self.random_second_number*self.random_second_number 138 ) 139 self.assertEqual( 140 src.calculator.subtract( 141 two_numbers[-2], 142 two_numbers[0] 143 ), 144 self.random_first_number-self.random_second_number 145 )the terminal shows AssertionError
AssertionError: 0.0 != FGH.IJKLMNOPQRSTUI change the expectation to match
139 self.assertEqual( 140 src.calculator.subtract( 141 two_numbers[-2], 142 two_numbers[0] 143 ), 144 self.random_first_number-self.random_first_number 145 ) 146 147 148# Exceptions seenthe test passes.
two_numbersis a list with two items -self.random_first_numberandself.random_second_number, this means
two_numbers[-2]isself.random_first_numbertwo_numbers[0]isself.random_first_number
test calculator with * expression
RED: make it fail
I can use a starred expression to unpack the list in an assertion like the positional arguments in test_functions_w_unknown_arguments
139 self.assertEqual(
140 src.calculator.subtract(
141 two_numbers[-2],
142 two_numbers[0]
143 ),
144 self.random_first_number-self.random_first_number
145 )
146 self.assertEqual(
147 src.calculator.add(*two_numbers),
148 self.random_first_number-self.random_second_number
149 )
the terminal shows AssertionError
AssertionError: GHI.JKLMNOPQRSTUVW != XYZ.ABCDEFGHIJKLMN
GREEN: make it pass
I change the expectation to match the result of adding the two numbers from the list
146 self.assertEqual(
147 src.calculator.add(*two_numbers),
148 self.random_first_number+self.random_second_number
149 )
the test passes. The starred expression gives the items of the list in the same order every time, this means these statements are the same
src.calculator.add(*two_numbers)
src.calculator.add(self.random_first_number, self.random_second_number)
because two_numbers is a list with two items - self.random_first_number and self.random_second_number
REFACTOR: make it better
I add another assertion for division
146 self.assertEqual( 147 src.calculator.add(*two_numbers), 148 self.random_first_number+self.random_second_number 149 ) 150 self.assertEqual( 151 src.calculator.divide(*two_numbers), 152 self.random_first_number*self.random_second_number 153 )the terminal shows AssertionError
AssertionError: H.IJKLMNOPQRSTUVWX != YZABCD.EFGHIJKLMNOI change the calculation
150 self.assertEqual( 151 src.calculator.divide(*two_numbers), 152 self.random_first_number/self.random_second_number 153 )the test passes
I add an assertion for the multiply function
150 self.assertEqual( 151 src.calculator.divide(*two_numbers), 152 self.random_first_number/self.random_second_number 153 ) 154 self.assertEqual( 155 src.calculator.multiply(*two_numbers), 156 self.random_first_number/self.random_second_number 157 )the terminal shows AssertionError
AssertionError: IJKLMN.OPQRSTUVWX != Y.ZABCDEFGHIJKLMNOPI change the calculation in the expectation to multiplication
154 self.assertEqual( 155 src.calculator.multiply(*two_numbers), 156 self.random_first_number*self.random_second_number 157 )the test passes
I add the next assertion
154 self.assertEqual( 155 src.calculator.multiply(*two_numbers), 156 self.random_first_number*self.random_second_number 157 ) 158 self.assertEqual( 159 src.calculator.subtract(*two_numbers), 160 self.random_first_number+self.random_second_number 161 )the terminal shows AssertionError
AssertionError: JKL.MNOPQRSTUVWXYZ != ABC.DEFGHIJKLMNOPI change the expectation to subtraction
112 def test_calculator_w_list_items(self): 113 two_numbers = [ 114 self.random_first_number, 115 self.random_second_number 116 ] 117 118 self.assertEqual( 119 src.calculator.add( 120 two_numbers[0], 121 two_numbers[1] 122 ), 123 self.random_first_number+self.random_second_number 124 ) 125 self.assertEqual( 126 src.calculator.divide( 127 two_numbers[-2], 128 two_numbers[-1] 129 ), 130 self.random_first_number/self.random_second_number 131 ) 132 self.assertEqual( 133 src.calculator.multiply( 134 two_numbers[1], 135 two_numbers[-1] 136 ), 137 self.random_second_number*self.random_second_number 138 ) 139 self.assertEqual( 140 src.calculator.subtract( 141 two_numbers[-2], 142 two_numbers[0] 143 ), 144 self.random_first_number-self.random_first_number 145 ) 146 self.assertEqual( 147 src.calculator.add(*two_numbers), 148 self.random_first_number+self.random_second_number 149 ) 150 self.assertEqual( 151 src.calculator.divide(*two_numbers), 152 self.random_first_number/self.random_second_number 153 ) 154 self.assertEqual( 155 src.calculator.multiply(*two_numbers), 156 self.random_first_number*self.random_second_number 157 ) 158 self.assertEqual( 159 src.calculator.subtract(*two_numbers), 160 self.random_first_number-self.random_second_number 161 ) 162 163 164# Exceptions seenthe test passes
test_calculator_raises_type_error_when_given_more_than_two_inputs
It is important to remember that the starred expression always gives the items from the list in order, and I cannot use a list that has more than 2 numbers 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 starred expression
158 self.assertEqual(
159 src.calculator.subtract(*two_numbers),
160 self.random_first_number-self.random_second_number
161 )
162
163 def test_calculator_raises_type_error_when_given_more_than_two_inputs(self):
164 not_two_numbers = [0, 1, 2]
165
166 src.calculator.add(*not_two_numbers)
167
168
169# Exceptions seen
TypeError: numbers_only.<locals>.wrapper() takes 2 positional arguments but 3 were given
GREEN: make it pass
I add the assertRaises method to handle the Exception
163 def test_calculator_raises_type_error_when_given_more_than_two_inputs(self):
164 not_two_numbers = [0, 1, 2]
165
166 with self.assertRaises(TypeError):
167 src.calculator.add(*not_two_numbers)
the test passes
REFACTOR: make it better
I add a failing line for division with the new list
163 def test_calculator_raises_type_error_when_given_more_than_two_inputs(self): 164 not_two_numbers = [0, 1, 2] 165 166 with self.assertRaises(TypeError): 167 src.calculator.add(*not_two_numbers) 168 src.calculator.divide(*not_two_numbers)TypeError: numbers_only.<locals>.wrapper() takes 2 positional arguments but 3 were givenI add assertRaises
166 with self.assertRaises(TypeError): 167 src.calculator.add(*not_two_numbers) 168 with self.assertRaises(TypeError): 169 src.calculator.divide(*not_two_numbers)the test passes
I add a line for multiplication
168 with self.assertRaises(TypeError): 169 src.calculator.divide(*not_two_numbers) 170 src.calculator.multiply(*not_two_numbers)TypeError: numbers_only.<locals>.wrapper() takes 2 positional arguments but 3 were givenI add assertRaises
168 with self.assertRaises(TypeError): 169 src.calculator.divide(*not_two_numbers) 170 with self.assertRaises(TypeError): 171 src.calculator.multiply(*not_two_numbers)the test passes
I add the last line
170 with self.assertRaises(TypeError): 171 src.calculator.multiply(*not_two_numbers) 172 src.calculator.subtract(*not_two_numbers)TypeError: numbers_only.<locals>.wrapper() takes 2 positional arguments but 3 were givenI handle the Exception
163 def test_calculator_raises_type_error_when_given_more_than_two_inputs(self): 164 not_two_numbers = [0, 1, 2] 165 166 with self.assertRaises(TypeError): 167 src.calculator.add(*not_two_numbers) 168 with self.assertRaises(TypeError): 169 src.calculator.divide(*not_two_numbers) 170 with self.assertRaises(TypeError): 171 src.calculator.multiply(*not_two_numbers) 172 with self.assertRaises(TypeError): 173 src.calculator.subtract(*not_two_numbers) 174 175 176# Exceptions seenthe test passes
close the project
I close
test_calculator.pyandcalculator.pyin the editorsI click in the terminal, then use q on the keyboard to leave the tests. The terminal goes back to the command line
I change directory to the parent of
calculatorcd ..the terminal shows
.../pumping_pythonI am back in the
pumping_pythondirectory
review
I added these tests to the calculator program after testing lists
code from the chapter
what is next?
you know
would you like to test list comprehensions? They are a quick way to make lists
rate pumping python
If this has been a 7 star experience for you, please CLICK HERE to leave a 5 star review of pumping python. It helps other people get into the book too