how to make a calculator
I want to write a program that can add, subtract, multiply and divide
preview
Here are the tests I have by the end of the chapter
1import random
2import src.calculator
3import unittest
4
5
6def a_random_number():
7 return random.triangular(-10, 10)
8
9
10class TestCalculator(unittest.TestCase):
11
12 random_first_number = a_random_number()
13 random_second_number = a_random_number()
14
15 def test_addition(self):
16 self.assertEqual(
17 src.calculator.add(
18 self.random_first_number,
19 self.random_second_number
20 ),
21 self.random_first_number+self.random_second_number
22 )
23
24 def test_subtraction(self):
25 self.assertEqual(
26 src.calculator.subtract(
27 self.random_first_number,
28 self.random_second_number
29 ),
30 self.random_first_number-self.random_second_number
31 )
32
33 def test_multiplication(self):
34 self.assertEqual(
35 src.calculator.multiply(
36 self.random_first_number,
37 self.random_second_number
38 ),
39 self.random_first_number*self.random_second_number
40 )
41
42 def test_division(self):
43 self.assertEqual(
44 src.calculator.divide(
45 self.random_first_number,
46 self.random_second_number
47 ),
48 self.random_first_number/self.random_second_number
49 )
50
51
52# Exceptions seen
53# AssertionError
54# NameError
55# AttributeError
56# TypeError
start the project
I name this project
calculatorI open a terminal
then I make a directory for the project
mkdir calculatorthe terminal goes back to the command line
.../pumping_pythonI change directory to the project
cd calculatorthe terminal shows I am now in the
calculatorfolder.../pumping_python/calculatorI make a folder for the source code
mkdir srcthe terminal goes back to the command line
.../pumping_python/calculatorI use touch to make an empty file for the program in the
srcfoldertouch src/calculator.pyon Windows without Windows Subsystem for Linux use
New-Item src/calculator.pyinstead oftouch src/calculator.pyNew-Item src/calculator.pythe terminal goes back to the command line
.../pumping_python/calculatorI make a directory for the tests
mkdir teststhe terminal goes back to the command line
I use touch to make an empty file in the
testsfolder to tell Python that it is a Python packageAttention
use 2 underscores (__) before and after
initfor__init__.pynot_init_.pytouch tests/__init__.pyon Windows without Windows Subsystem for Linux use
New-Item tests/__init__.pyinstead oftouch tests/__init__.pyNew-Item tests/__init__.pythe terminal goes back to the command line
I make an empty file for the actual test
touch tests/test_calculator.pyon Windows without Windows Subsystem for Linux use
New-Item tests/test_calculator.pyinstead oftouch tests/test_calculator.pyNew-Item tests/test_calculator.pythe terminal goes back to the command line
I open
test_calculator.pyin the editor of the Integrated Development Environment (IDE)Tip
I can open a file from the terminal in Visual Studio Code by typing
codeand the name of the file withcode tests/test_calculator.pytest_calculator.pyopens up in the editorI add the first failing test to
test_calculator.py1import unittest 2 3 4class TestCalculator(unittest.TestCase): 5 6 def test_failure(self): 7 self.assertFalse(True)I make a virtual environment in the terminal
python3 -m venv .venvon Windows without Windows Subsystem for Linux use
python3 -m venv .venvinstead ofpython3 -m venv .venvpython -m venv .venvthe terminal takes some time then goes back to the command line
I activate the virtual environment
source .venv/bin/activateon 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 upgrade the Python package manager (pip) to the latest version
python3 -m pip install --upgrade pipthe terminal shows pip being uninstalled then installs the latest version or shows that it is already the latest version
I make a
requirements.txtfile for the Python programs my project needsecho "pytest-watch" > requirements.txtthe terminal goes back to the command line
I use pip to use the requirements file to install
pytest-watchpython3 -m pip install --requirement requirements.txton Windows without Windows Subsystem for Linux use
python -m pip install --requirement requirements.txtinstead ofpython3 -m pip install --requirement requirements.txtpython -m pip install --requirement requirements.txtthe terminal shows pip downloads and installs the Python programs that pytest-watch needs to run
I use pytest-watch to run the test
pytest-watchthe terminal shows
================================ FAILURES ================================ _____________________ TestCalculator.test_failure ________________________ self = <tests.test_calculator.TestCalculator testMethod=test_failure> def test_failure(self): > self.assertFalse(True) E AssertionError: True is not false tests/test_calculator.py:7: AssertionError ======================== short test summary info ========================= FAILED tests/test_calculator.py::TestCalculator::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 ontests/test_calculator.py:7to open it in the editorI add AssertionError to the list of Exceptions seen in
test_calculator.py7 self.assertFalse(True) 8 9 10# Exceptions seen 11# AssertionErrorthen I change True to False in the assertion
7 self.assertFalse(False)the test passes
I add a TODO list to keep track of the work for the program
1import unittest 2 3 4class TestCalculator(unittest.TestCase): 5 6 def test_failure(self): 7 self.assertFalse(False) 8 9 10# TODO 11# test addition 12# test subtraction 13# test multiplication 14# test division 15 16 17# Exceptions seen 18# AssertionError
test_addition
RED: make it fail
I change
test_failuretotest_additionthen change assertFalse to assertEqual1import unittest 2 3 4class TestCalculator(unittest.TestCase): 5 6 def test_addition(self): 7 self.assertEqual( 8 src.calculator.add(0, 1), 9 1 10 )the assertEqual method from AssertionError checks if the 2 things in parentheses are the same. It is like the statement
assert x == yor askingis x equal to y?the explanation I like from what I have seen is that one of them is
reality-src.calculator.add(0, 1), and the other ismy
expectation-1, because0 + 1is1
NameError: name 'src' is not definedbecause
srcis not defined intest_calculator.py
GREEN: make it pass
I add NameError to the list of Exceptions seen in
test_calculator.py20# Exceptions seen 21# AssertionError 22# NameErrorI add an import statement at the top of the file for the
calculatormodule1import src.calculator 2import unittest 3 4 5class TestCalculator(unittest.TestCase):the terminal shows AttributeError
AttributeError: module 'src.calculator' has no attribute 'add'I think of
src.calculator.addas an address,addis something (an attribute) in the emptycalculator.pyfile from thesrcfolder (directory)I add AttributeError to the list of Exceptions seen in
test_calculator.py21# Exceptions seen 22# AssertionError 23# NameError 24# AttributeErrorI open
calculator.pyfrom thesrcfolder in the editor, and I type the name1addNameError: name 'add' is not definedI have to tell Python what the name
addstands for or meansI point it to None
1add = NoneTypeError: 'NoneType' object is not callablebecause the
addvariable is now a name for None which I cannot use like a functionI add TypeError to the list of Exceptions seen in
test_calculator.py21# Exceptions seen 22# AssertionError 23# NameError 24# AttributeError 25# TypeErrorI use the def keyword in
calculator.pyto makeadda function so it is callable1def add(): 2 return NoneTypeError: add() takes 0 positional arguments but 2 were giventhe definition of
adddoes not allow it take input, but 2 were given in the callsrc.calculator.add(0, 1):0and1I make the
addfunction take 2 inputs1def add(first_input, second_input): 2 return Nonethe terminal shows AssertionError
AssertionError: None != 1I make the return statement match the expected value
1def add(first_input, second_input): 2 return 1the test passes, time for a victory lap!
REFACTOR: make it better
The add function passes the test but does not meet the actual requirement because it always returns 1. I want it to return the result of a calculation with the inputs
RED: make it fail
To show the problem with the function, I add another assertion in test_calculator.py
7 def test_addition(self):
8 self.assertEqual(
9 src.calculator.add(0, 1),
10 1
11 )
12 self.assertEqual(
13 src.calculator.add(0, 2),
14 2
15 )
the terminal shows AssertionError
E AssertionError: 1 != 2
the function returns 1, the test expects 2
GREEN: make it pass
when I change the return statement in
calculator.pyto match the expectation1def add(first_input, second_input): 2 return 2the terminal shows AssertionError
AssertionError: 2 != 1this makes the assertion that was passing before now fail. I need a solution that can make the two tests pass
I make the function return the result of adding the two inputs
1def add(first_input, second_input): 2 return first_input + second_inputthe test passes. The
addfunction passes the two tests
RED: make it fail
I add another test to make sure the function works for other numbers
12 self.assertEqual(
13 src.calculator.add(0, 2),
14 2
15 )
16 self.assertEqual(
17 src.calculator.add(0, 3),
18 2
19 )
the terminal shows AssertionError
AssertionError: 3 != 2
GREEN: make it pass
I change the expectation in the test
16 self.assertEqual(
17 src.calculator.add(0, 3),
18 3
19 )
the test passes
RED: make it fail
I add another test with a different number for the first input
16 self.assertEqual(
17 src.calculator.add(0, 3),
18 3
19 )
20 self.assertEqual(
21 src.calculator.add(1, 3),
22 3
23 )
the terminal shows AssertionError
AssertionError: 4 != 3
GREEN: make it pass
I change the expectation to match reality
20 self.assertEqual(
21 src.calculator.add(1, 3),
22 4
23 )
the test passes. The add function looks good so far
RED: make it fail
I add a test with bigger numbers
20 self.assertEqual(
21 src.calculator.add(1, 3),
22 4
23 )
24 self.assertEqual(
25 src.calculator.add(123456, 789012),
26 4
27 )
the terminal shows AssertionError
AssertionError: 912468 != 4
GREEN: make it pass
I change the expectation to match reality
24 self.assertEqual(
25 src.calculator.add(123456, 789012),
26 912468
27 )
the test passes
RED: make it fail
I add another test, this time with a negative number
24 self.assertEqual(
25 src.calculator.add(123456, 789012),
26 912468
27 )
28 self.assertEqual(
29 src.calculator.add(-1, 0),
30 912468
31 )
the terminal shows AssertionError
AssertionError: -1 != 912468
GREEN: make it pass
I change the expectation to match reality
28 self.assertEqual(
29 src.calculator.add(-1, 0),
30 -1
31 )
the test passes
RED: make it fail
I try another test with two negative numbers
28 self.assertEqual(
29 src.calculator.add(-1, 0),
30 -1
31 )
32 self.assertEqual(
33 src.calculator.add(-2, -3),
34 -1
35 )
the terminal shows AssertionError
AssertionError: -5 != -1
GREEN: make it pass
I make the expectation match reality
32 self.assertEqual(
33 src.calculator.add(-2, -3),
34 -5
35 )
the test passes. The add function can handle positive and negative numbers
RED: make it fail
I add another test with floats (binary floating point decimal numbers)
32 self.assertEqual(
33 src.calculator.add(-2, -3),
34 -5
35 )
36 self.assertEqual(
37 src.calculator.add(0.1234, -5.6789),
38 -5
39 )
the terminal shows AssertionError
AssertionError: -5.555499999999999 != -5
GREEN: make it pass
I change the expectation
36 self.assertEqual(
37 src.calculator.add(0.1234, -5.6789),
38 -5.555499999999999
39 )
the test passes. Why is the result -5.555499999999999 instead of 5.5555?
what is a variable?
I just did the same kind of calculation 8 times in a row, there is a better way to this, you have seen this in Algebra. I can use a letter or a name for the numbers, that way I can have one test which covers all possible numbers. I can do this with a variable.
A variable is a name that is used for values that change. For example, in the tests so far, I have
src.calculator.add(0, 1) is 0+1 is 1
src.calculator.add(0, 2) is 0+2 is 2
src.calculator.add(0, 3) is 0+3 is 3
src.calculator.add(1, 3) is 1+3 is 4
src.calculator.add(123456, 789012) is 123456+789012 is 912468
src.calculator.add(-1, 0) is -1+0 is -1
src.calculator.add(-2, -3) is -2+-3 is -5
src.calculator.add(0.1234, -5.6789) is 0.1234+-5.6789 is -5.555499999999999
all of these lines can be written using first_number as the name of the first number and second_number as the name for the second number, like this
src.calculator.add(first_number, second_number) is first_number+second_number
I add a new test at the beginning of
test_additionwith names for the values5class TestCalculator(unittest.TestCase): 6 7 def test_addition(self): 8 self.assertEqual( 9 src.calculator.add(first_number, second_number), 10 first_number+second_number 11 ) 12 self.assertEqual( 13 src.calculator.add(0, 1), 14 1 15 )NameError: name 'first_number' is not definedI have to tell Python what the value of
first_numberisI point
first_numberto0before the test7 def test_addition(self): 8 first_number = 0 9 10 self.assertEqual( 11 src.calculator.add(first_number, second_number), 12 first_number+second_number 13 )NameError: name 'second_number' is not definedI have to tell Python what the value of
second_numberisI point
second_numberto1before the test7 def test_addition(self): 8 first_number = 0 9 second_number = 1 10 11 self.assertEqual( 12 src.calculator.add(first_number, second_number), 13 first_number+second_number 14 )the test passes
I remove the next test since it is now covered by this new assertion that uses a variable
11 self.assertEqual( 12 src.calculator.add(first_number, second_number), 13 first_number+second_number 14 ) 15 self.assertEqual( 16 src.calculator.add(0, 2), 17 2 18 )I can do the same thing for all the other tests by changing the values for
first_inputandsecond_input, for example8 first_number = 0 9 second_number = 2the test is still green
8 first_number = 1 9 second_number = 3still green
8 first_number = 123456 9 second_number = 789012the terminal still shows green
8 first_number = 0.1234 9 second_number = -5.6789the test is still passing. The problem with this is I lose the test for the previous number, everytime I change a number. I need a better way
I want to use random numbers for
first_inputandsecond_inputto make sure that theaddfunction always returns the result of adding the two numbers without knowing what the numbers will be. I can do this with the random module from the Python standard library. I add an import statement for it at the top oftest_calculator.py1import random 2import src.calculator 3import unittestrandom is a module from the Python standard library that is used to make fake random numbers
I use a random value for
first_number8 def test_addition(self): 9 # first_number = 0.1234 10 first_number = random.triangular(-0.1, 1.0) 11 second_number = -5.6789the test is still green. random.triangular returns a random float that could be any number from
-0.1to1.0in this caseI want to see the test fail to be sure everything works as expected. I change the expectation in the first assertion
13 self.assertEqual( 14 src.calculator.add(first_number, second_number), 15 first_number+first_number 16 )I hit save (ctrl+s (Windows/Linux) or command+s (mac)) a few times in the editor to run the tests and the terminal AssertionError with random values that look like this
AssertionError: -X.YZABCDEFGHIJKLM != A.BCDEFGHIJKLMNOPQI change the expectation of the assertion back to the right calculation
13 self.assertEqual( 14 src.calculator.add(first_number, second_number), 15 first_number+second_number 16 )the test is green again
I remove the commented line then use a random value for
second_number8 def test_addition(self): 9 first_number = random.triangular(-0.1, 1.0) 10 # second_number = -5.6789 11 second_number = random.triangular(-0.1, 1.0)the test is still green
I remove the commented line and the other assertions because they are covered by the one that uses random numbers. I do not need them anymore
8 def test_addition(self): 9 first_number = random.triangular(-0.1, 1.0) 10 second_number = random.triangular(-0.1, 1.0) 11 12 self.assertEqual( 13 src.calculator.add(first_number, second_number), 14 first_number+second_number 15 ) 16 17 18# TODOstill green
I change the name of the variables to be more clear
8def test_addition(self): 9 random_first_number = random.triangular(-0.1, 1.0) 10 random_second_number = random.triangular(-0.1, 1.0) 11 12 self.assertEqual( 13 src.calculator.add( 14 random_first_number, 15 random_second_number 16 ), 17 random_first_number+random_second_number 18 )the test is still green
There is some duplication, I have to make a change in more than one place when I want to use a different range of random numbers for the test, for example
8 def test_addition(self): 9 random_first_number = random.triangular(-10.0, 10.0) 10 random_second_number = random.triangular(-10.0, 10.0)the test is still green
I add a function to remove the repetition
1import random 2import src.calculator 3import unittest 4 5 6def a_random_number(): 7 return random.triangular(-10.0, 10.0) 8 9 10class TestCalculator(unittest.TestCase):then I use the new function to get random values for the
random_first_numberandrandom_second_numbervariables12 def test_addition(self): 13 # random_first_number = random.triangular(-10.0, 10.0) 14 random_first_number = a_random_number() 15 # random_second_number = random.triangular(-10.0, 10.0) 16 random_second_number = a_random_number()the terminal still shows green
I remove the commented lines
12 def test_addition(self): 13 random_first_number = a_random_number() 14 random_second_number = a_random_number()I now only need to change the range of random numbers for the test in one place
6def a_random_number(): 7 return random.triangular(-10000.0, 10000.0)and the terminal still shows green
I can use any range of numbers the computer can handle, for example
6def a_random_number(): 7 return random.triangular(-10.0**100000, 10.0**100000)the terminal shows OverflowError
OverflowError: (34, 'Numerical result out of range')because the numbers are too big to be used
**is the symbol for raise to the power (exponents)10.0**100000is how to write10.0raised to the power of100,000
I make the range smaller
6def a_random_number(): 7 return random.triangular(-1000.0, 1000.0)the test is still green, though the test takes a little longer to run
then I remove
test additionfrom the TODO list22# TODO 23# test subtraction 24# test multiplication 25# test division
test_subtraction
RED: make it fail
I add a test for subtraction in test_calculator.py
12 def test_addition(self):
13 random_first_number = a_random_number()
14 random_second_number = a_random_number()
15
16 self.assertEqual(
17 src.calculator.add(
18 random_first_number,
19 random_second_number
20 ),
21 random_first_number+random_second_number
22 )
23
24 def test_subtraction(self):
25 random_first_number = a_random_number()
26 random_second_number = a_random_number()
27
28 self.assertEqual(
29 src.calculator.subtract(
30 random_first_number,
31 random_second_number
32 ),
33 random_first_number-random_second_number
34 )
35
36
37# TODO
the terminal shows AttributeError
AttributeError: module 'src.calculator' has no attribute 'subtract'
calculator.py in the src folder does not have anything named subtract in it
GREEN: make it pass
I add the name to
calculator.py1def add(first_input, second_input): 2 return first_input + second_input 3 4 5subtractNameError: name 'subtract' is not definedI point
subtractto None5subtract = NoneTypeError: 'NoneType' object is not callableI have seen this before
I change
subtractto a function to make it callable5def subtract(): 6 return NoneTypeError: subtract() takes 0 positional arguments but 2 were givenI make
subtracttake inputs5def subtract(first_input, second_input): 6 return NoneI hit save (ctrl+s (Windows/Linux) or command+s (mac)) a few times in the editor to run the tests and the terminal shows AssertionError with random values that look like this
the terminal shows AssertionError
AssertionError: None != XYZ.ABCDEFGHIJKLMNOPsubtractreturns None, the test expectsrandom_first_number-random_second_numberorfirst_input-second_input- the difference between the 2 numbersI make the
subtractfunction return the difference between the inputs5def subtract(first_input, second_input): 6 return first_input - second_inputthe test passes. SUCCESS!
REFACTOR: make it better
I have some duplication to remove, the code below happens twice in
test_calculator.pyrandom_first_number = a_random_number() random_second_number = a_random_number()once in
test_additionand again intest_subtractionI add class attributes (variables) to remove the duplication and use the same numbers for both tests
10class TestCalculator(unittest.TestCase): 11 12 random_first_number = a_random_number() 13 random_second_number = a_random_number() 14 15 def test_addition(self): 16 random_first_number = a_random_number() 17 random_second_number = a_random_number()I use the new class attributes in
test_addition15 def test_addition(self): 16 # random_first_number = a_random_number() 17 random_first_number = self.random_first_number 18 # random_second_number = a_random_number() 19 random_second_number = self.random_second_number 20 21 self.assertEqual( 22 src.calculator.add( 23 random_first_number, 24 random_second_number 25 ), 26 random_first_number+random_second_number 27 )and in
test_subtraction29 def test_subtraction(self): 30 # random_first_number = a_random_number() 31 random_first_number = self.random_first_number 32 # random_second_number = a_random_number() 33 random_second_number = self.random_second_number 34 35 self.assertEqual( 36 src.calculator.subtract( 37 random_first_number, 38 random_second_number 39 ), 40 random_first_number-random_second_number 41 )the terminal shows the tests are still passing. The
random_first_numberandrandom_second_numbervariables are made once as class attributes and used later in each test withself.random_first_numberandself.random_second_number, the same way I use unittest.TestCase methods like assertEqual or assertFalseI remove the commented lines in
test_addition15 def test_addition(self): 16 random_first_number = self.random_first_number 17 random_second_number = self.random_second_numberand do the same thing in
test_subtraction27 def test_subtraction(self): 28 random_first_number = self.random_first_number 29 random_second_number = self.random_second_numberI can use the class attributes directly in
test_addition19 self.assertEqual( 20 src.calculator.add( 21 # random_first_number, 22 self.random_first_number, 23 # random_second_number 24 self.random_second_number 25 ), 26 # random_first_number+random_second_number 27 self.random_first_number+self.random_second_number 28 )the test is still green. I remove the commented lines
19 self.assertEqual( 20 src.calculator.add( 21 self.random_first_number, 22 self.random_second_number 23 ), 24 self.random_first_number+self.random_second_number 25 )I do the same thing in
test_subtraction31 self.assertEqual( 32 src.calculator.subtract( 33 # random_first_number, 34 self.random_first_number, 35 # random_second_number 36 self.random_second_number 37 ), 38 # random_first_number-random_second_number 39 self.random_first_number-self.random_second_number 40 )the test is still passing. I remove the commented lines
31 self.assertEqual( 32 src.calculator.subtract( 33 self.random_first_number, 34 self.random_second_number 35 ), 36 self.random_first_number-self.random_second_number 37 )I remove the
first_random_numberandsecond_random_numbervariables fromtest_additionandtest_subtractionbecause they are no longer used10class TestCalculator(unittest.TestCase): 11 12 random_first_number = a_random_number() 13 random_second_number = a_random_number() 14 15 def test_addition(self): 16 self.assertEqual( 17 src.calculator.add( 18 self.random_first_number, 19 self.random_second_number 20 ), 21 self.random_first_number+self.random_second_number 22 ) 23 24 def test_subtraction(self): 25 self.assertEqual( 26 src.calculator.subtract( 27 self.random_first_number, 28 self.random_second_number 29 ), 30 self.random_first_number-self.random_second_number 31 ) 32 33 34# TODOand the tests are still green!
I remove
test subtractionfrom the TODO list34# TODO 35# test multiplication 36# test division 37 38 39# Exceptions seen
test_multiplication
RED: make it fail
I add a failing test for multiplication in test_calculator.py
24 def test_subtraction(self):
25 self.assertEqual(
26 src.calculator.subtract(
27 self.random_first_number,
28 self.random_second_number
29 ),
30 self.random_first_number-self.random_second_number
31 )
32
33 def test_multiplication(self):
34 self.assertEqual(
35 src.calculator.multiply(
36 self.random_first_number,
37 self.random_second_number
38 ),
39 self.random_first_number*self.random_second_number
40 )
41
42
43# TODO
the terminal shows AttributeError
AttributeError: module 'src.calculator' has no attribute 'multiply'
GREEN: make it pass
using what I know so far, I add a function to calculator.py
5def subtract(first_input, second_input):
6 return first_input - second_input
7
8
9def multiply(first_input, second_input):
10 return first_input * second_input
the test passes! I remove test_multiplication from the TODO list in test_calculator.py
43# TODO
44# test division
45
46
47# Exceptions seen
*is the symbol for multiplication**is the symbol for raise to the power (exponent)
test_division
RED: make it fail
Time for division. I add a new test to test_calculator.py
33 def test_multiplication(self):
34 self.assertEqual(
35 src.calculator.multiply(
36 self.random_first_number,
37 self.random_second_number
38 ),
39 self.random_first_number*self.random_second_number
40 )
41
42 def test_division(self):
43 self.assertEqual(
44 src.calculator.divide(
45 self.random_first_number,
46 self.random_second_number
47 ),
48 self.random_first_number/self.random_second_number
49 )
50
51
52# TODO
the terminal shows AttributeError
AttributeError: module 'src.calculator' has no attribute 'divide'
GREEN: make it pass
I add a function to
calculator.py9def multiply(first_input, second_input): 10 return first_input * second_input 11 12 13def divide(first_input, second_input): 14 return first_input / second_inputthe test passes
I remove the TODO list
42 def test_division(self): 43 self.assertEqual( 44 src.calculator.divide( 45 self.random_first_number, 46 self.random_second_number 47 ), 48 self.random_first_number/self.random_second_number 49 ) 50 51 52# Exceptions seen 53# AssertionError 54# NameError 55# AttributeError 56# TypeError 57# ZeroDivisionError
test_calculator_tests
Since everything is green, I can write the program that makes the tests pass without looking at them
RED: make it fail
I close
test_calculator.pythen delete all the text in
calculator.py, the terminal shows 4 failures, I start with the last AttributeErrorAttributeError: module 'src.calculator' has no attribute 'subtract'What other Exceptions do you think are raised as I go along?
GREEN: make it pass
I add the name to
calculator.py1subtractNameError: name 'subtract' is not definedI point it to None
1subtract = NoneTypeError: 'NoneType' object is not callableI change
subtractto a function1def subtract(): 2 return NoneTypeError: subtract() takes 0 positional arguments but 2 were givenI add positional arguments to the function
1def subtract(first_input, second_input): 2 return Nonethe terminal shows AssertionError with random numbers
AssertionError: None != XYZ.ABCDEFGHIJKLMNI change the return statement to see the difference between the inputs and expected output, remember the identity function?
1def subtract(first_input, second_input): 2 return first_input, second_inputthe terminal shows AssertionError with random numbers that look like this
AssertionError: (XYZ.ABCDEFGHIJKLMN, YZA.BCDEFGHIJKLMNO) != ZAB.CDEFGHIJKLMNOPthe name of the function is
subtractand the test expects the difference between the 2 inputsI make the return statement match the expectation
1def subtract(first_input, second_input): 2 return first_input - second_inputthe terminal shows AttributeError
AttributeError: module 'src.calculator' has no attribute 'multiply'I add a function
1def subtract(first_input, second_input): 2 return first_input - second_input 3 4 5def multiply(): 6 return NoneTypeError: multiply() takes 0 positional arguments but 2 were givenI add 2 variables for the positional arguments
5def multiply(first_input, second_input): 6 return Nonethe terminal shows AssertionError
AssertionError: None != XY.ZABCDEFGHIJKLMI change the return statement to see the difference between the inputs and the expected output, identity function again
5def multiply(first_input, second_input): 6 return first_input, second_inputthe terminal shows AssertionError with random numbers that look like this
AssertionError: (XYZ.ABCDEFGHIJKLMNO, -YZA.BCDEFGHIJKLMNOPQ) != -ZAB.CDEFGHIJKLMNOPQRI change it to the multiplication of the inputs to match the name of the function
5def multiply(first_input, second_input): 6 return first_input * second_inputthe terminal shows AttributeError
AttributeError: module 'src.calculator' has no attribute 'divide'I add another function
5def multiply(first_input, second_input): 6 return first_input * second_input 7 8 9def divide(first_input, second_input): 10 return first_input, second_inputthe terminal shows AssertionError with random numbers that look like this
AssertionError: (-XYZ.ABCDEFGHIJKLMNO, YZA.BCDEFGHIJKLMNOPQ) != -ZAB.CDEFGHIJKLMNOPQRwhen I change the return statement to match the expectation
9def divide(first_input, second_input): 10 return first_input / second_inputthe terminal shows AttributeError
AttributeError: module 'src.calculator' has no attribute 'add'the return statement of the last 3 functions matched their names, I do the same thing for the new one
1def subtract(first_input, second_input): 2 return first_input - second_input 3 4 5def multiply(first_input, second_input): 6 return first_input * second_input 7 8 9def divide(first_input, second_input): 10 return first_input / second_input 11 12 13def add(first_input, second_input): 14 return first_input + second_inputand all the tests are passing with no random failures, or are they?
close the project
I close the file(s) I have open in the editor(s)
I click in the terminal and exit the tests with ctrl+c on the keyboard
I 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 wrote the following tests for a program that can add, subtract, multiply and divide
I also saw the following Exceptions
code from the chapter
what is next?
you know a lot
There is a problem, I have done the same steps for each of the 8 chapters covered so far
I give the project a name
I make a Python file to hold the source code in the ‘src’ folder
I make a Python file to hold the tests in the ‘tests’ folder
I make the test pass
then I start working on the project
I think I know how to make a Python Test Driven Development environment. I am going to write a program that will do all the steps for making a project for me, so I never have to do those steps again.
Would you like to know how to make a Python Test Driven Development environment automatically?