Automated Teller Machine
I want to make an Automated Teller Machine that gives me CASH or DENIED when I try to make a withdrawal.
preview
These are the tests I have at the end of the chapter
1import src.atm
2import unittest
3
4
5DENIED = 'DENIED'
6
7
8class TestATM(unittest.TestCase):
9
10 def test_card_w_below_limit_right_pin_enough_cash(self):
11 self.assertEqual(
12 src.atm.withdraw(
13 right_pin=True,
14 enough_cash=True,
15 above_daily_limit=False,
16 card_expired=False,
17 ),
18 'CASH'
19 )
20 self.assertEqual(
21 src.atm.withdraw(
22 right_pin=True,
23 enough_cash=True,
24 above_daily_limit=False,
25 card_expired=True,
26 ),
27 DENIED
28 )
30 def test_card_w_above_limit_right_pin_enough_cash(self):
31 self.assertEqual(
32 src.atm.withdraw(
33 right_pin=True,
34 enough_cash=True,
35 above_daily_limit=True,
36 card_expired=True,
37 ),
38 DENIED
39 )
40 self.assertEqual(
41 src.atm.withdraw(
42 right_pin=True,
43 enough_cash=True,
44 above_daily_limit=True,
45 card_expired=False,
46 ),
47 DENIED
48 )
50 def test_card_w_above_limit_right_pin_not_enough_cash(self):
51 self.assertEqual(
52 src.atm.withdraw(
53 right_pin=True,
54 enough_cash=False,
55 above_daily_limit=True,
56 card_expired=True,
57 ),
58 DENIED
59 )
60 self.assertEqual(
61 src.atm.withdraw(
62 right_pin=True,
63 enough_cash=False,
64 above_daily_limit=True,
65 card_expired=False,
66 ),
67 DENIED
68 )
70 def test_card_w_below_limit_right_pin_not_enough_cash(self):
71 self.assertEqual(
72 src.atm.withdraw(
73 right_pin=True,
74 enough_cash=False,
75 above_daily_limit=False,
76 card_expired=True,
77 ),
78 DENIED
79 )
80 self.assertEqual(
81 src.atm.withdraw(
82 right_pin=True,
83 enough_cash=False,
84 above_daily_limit=False,
85 card_expired=False,
86 ),
87 DENIED
88 )
90 def test_card_w_above_limit_wrong_pin_enough_cash(self):
91 self.assertEqual(
92 src.atm.withdraw(
93 right_pin=False,
94 enough_cash=True,
95 above_daily_limit=True,
96 card_expired=True,
97 ),
98 DENIED
99 )
100 self.assertEqual(
101 src.atm.withdraw(
102 right_pin=False,
103 enough_cash=True,
104 above_daily_limit=True,
105 card_expired=False,
106 ),
107 DENIED
108 )
110 def test_card_w_below_limit_wrong_pin_enough_cash(self):
111 self.assertEqual(
112 src.atm.withdraw(
113 right_pin=False,
114 enough_cash=True,
115 above_daily_limit=False,
116 card_expired=True,
117 ),
118 DENIED
119 )
120 self.assertEqual(
121 src.atm.withdraw(
122 right_pin=False,
123 enough_cash=True,
124 above_daily_limit=False,
125 card_expired=False,
126 ),
127 DENIED
128 )
130 def test_card_w_above_limit_wrong_pin_not_enough_cash(self):
131 self.assertEqual(
132 src.atm.withdraw(
133 right_pin=False,
134 enough_cash=False,
135 above_daily_limit=True,
136 card_expired=True,
137 ),
138 DENIED
139 )
140 self.assertEqual(
141 src.atm.withdraw(
142 right_pin=False,
143 enough_cash=False,
144 above_daily_limit=True,
145 card_expired=False,
146 ),
147 DENIED
148 )
150 def test_card_w_below_limit_wrong_pin_not_enough_cash(self):
151 self.assertEqual(
152 src.atm.withdraw(
153 right_pin=False,
154 enough_cash=False,
155 above_daily_limit=False,
156 card_expired=True,
157 ),
158 DENIED
159 )
160 self.assertEqual(
161 src.atm.withdraw(
162 right_pin=False,
163 enough_cash=False,
164 above_daily_limit=False,
165 card_expired=False,
166 ),
167 DENIED
168 )
169
170
171# Exceptions seen
172# AssertionError
173# NameError
174# AttributeError
175# TypeError
176# SyntaxError
start the project
I open a terminal
I open
makePythonTdd.shI change the name of the project to
atminmakePythonTdd.sh1#!/bin/bash 2uv init atm 3cd atm 4mkdir tests 5touch tests/__init__.py 6 7echo "import unittest 8 9 10class TestATM(unittest.TestCase): 11 12 def test_failure(self): 13 self.assertFalse(True) 14 15 16# Exceptions seen 17# AssertionError 18" > tests/test_atm.py 19 20echo "pytest" > requirements.txt 21echo "pytest-watcher" >> requirements.txt 22uv add --requirement requirements.txt 23uv run pytest-watcher . --nowI run
makePythonTdd.shin the terminal to make theatmproject./makePythonTdd.sh
I open
makePythonTdd.ps1I change the name of the project to
atminmakePythonTdd.ps11uv init atm 2cd atm 3New-Item "src/atm.py" 4mkdir tests 5New-Item tests/__init__.py 6 7"import unittest 8 9 10class TestATM(unittest.TestCase): 11 12 def test_failure(self): 13 self.assertFalse(True) 14 15 16# Exceptions seen 17# AssertionError 18" | Out-File "tests/test_atm.py" -Encoding UTF8 19 20"pytest" | Out-File requirements.txt -Encoding UTF8 21"pytest-watcher" >> requirements.txt 22uv add --requirement requirements.txt 23uv run pytest-watcher . --nowI run
makePythonTdd.ps1in the terminal to make theatmproject.\makePythonTdd.ps1
the terminal is my friend, and shows AssertionError
======================== FAILURES ========================= _________________ TestATM.test_failure ____________________ self = <tests.test_atm.TestATM testMethod=test_failure> def test_failure(self): > self.assertFalse(True) E AssertionError: True is not false tests/test_atm.py:7: AssertionError ================ short test summary info ================== FAILED tests/test_atm.py::TestATM::test_failure - AssertionError: True is not false ==================== 1 failed in X.YZs ====================I hold ctrl (Windows/Linux) or option/command (MacOS) on the keyboard and use the mouse to click on
tests/test_atm.py:7to open itI change assertFalse to assertTrue in
test_atm.py4class TestATM(unittest.TestCase): 5 6 def test_failure(self): 7 # self.assertFalse(True) 8 self.assertTrue(True) 9 10 11# Exceptions seenthe test passes.
I open a new terminal then change directory to
atmcd atmI add the new files and folder to git for tracking
git add .I add a git commit message
git commit -am 'setup project'
If the inputs to the Automated Teller Machine are
is the PIN correct?
is there enough cash in the account for the withdrawal?
then I get this truth table
PIN |
cash |
withdrawal |
|---|---|---|
right PIN |
enough cash |
CASH |
right PIN |
NOT enough cash |
DENIED |
wrong PIN |
enough cash |
DENIED |
wrong PIN |
NOT enough cash |
DENIED |
test_right_pin_enough_cash
RED: make it fail
I change test_failure to test_right_pin_enough_cash, then add an assertion for when the right PIN is entered AND there is enough cash in the account
PIN |
cash |
withdrawal |
|---|---|---|
right PIN |
enough cash |
CASH |
4class TestATM(unittest.TestCase):
5
6 def test_right_pin_enough_cash(self):
7 my_expectation = 'CASH'
8 reality = src.atm.withdraw(
9 right_pin=True,
10 enough_cash=True,
11 )
12 self.assertEqual(reality, my_expectation)
13
14
15# Exceptions seen
16# AssertionError
the terminal is my friend, and shows NameError
NameError: name 'src' is not defined
because I do not have a definition for src in this file.
GREEN: make it pass
I add NameError to the list of Exceptions seen
15# Exceptions seen 16# AssertionError 17# NameErrorI add an import statement at the top of the file so that I can test
atm.pyfrom thesrcfolder1import src.atm 2import unittest 3 4 5class TestATM(unittest.TestCase):the terminal is my friend, and shows AttributeError
AttributeError: module 'src.atm' has no attribute 'withdraw'because
atm/__init__.pyin thesrcfolder does not have anything namedwithdrawin it.I add AttributeError to the list of Exceptions seen
16# Exceptions seen 17# AssertionError 18# NameError 19# AttributeErrorI open
atm/__init__.pyfrom thesrcfolderI delete all the text in the file then add a function named
withdrawtoatm.py1def withdraw(): 2 return Nonethe terminal is my friend, and shows TypeError
TypeError: withdraw() got an unexpected keyword argument 'right_pin'because the test called the
withdrawfunction with a name (right_pin) that is not in the parentheses of its definition.I add TypeError to the list of Exceptions seen, in
test_atm.py16# Exceptions seen 17# AssertionError 18# NameError 19# AttributeError 20# TypeErrorI add
right_pinto the function definition inatm.py1def withdraw(right_pin): 2 return Nonethe terminal is my friend, and shows TypeError
TypeError: withdraw() got an unexpected keyword argument 'enough_cash'because the test called the
withdrawfunction with a name (enough_cash) that is not in the parentheses of its definition.I add
enough_cashto the function signature1def withdraw(right_pin, enough_cash): 2 return Nonethe terminal is my friend, and shows AssertionError
AssertionError: None != 'CASH'the
withdrawfunction always returns None and I want'CASH'.I change the return statement to give me
'CASH'1def withdraw(right_pin, enough_cash): 2 return 'CASH'I add a git commit message in the other terminal
git commit -am \ 'add test_right_pin_enough_cash'
The withdraw function always returns ‘CASH’, it does not care about the inputs. Is this Tautology or ‘CASH’ that never ends?
withdraw(right_pin=True, enough_cash=True) -> 'CASH'
test_right_pin_not_enough_cash
RED: make it fail
I go back to the terminal where the tests are running
I add a test with an assertion for when the right PIN is entered AND there is NOT enough cash in the account
PIN
cash
withdrawal
right PIN
NOT enough cash
DENIED
13 self.assertEqual(reality, my_expectation) 14 15 def test_right_pin_not_enough_cash(self): 16 my_expectation = 'DENIED' 17 reality = src.atm.withdraw( 18 right_pin=True, 19 enough_cash=False, 20 ) 21 self.assertEqual(reality, my_expectation) 22 23 24# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: 'CASH' != 'DENIED'because the
withdrawfunction returns ‘CASH’ and the assertion expects ‘DENIED’. This ATM is broken, it should not give ‘CASH’ when there is NOT enough cash in the account.
GREEN: make it pass
I add an if statement to atm.py
1def withdraw(right_pin, enough_cash):
2 if enough_cash == False:
3 return 'DENIED'
4 return 'CASH'
the test passes.
REFACTOR: make it better
I use the bool built-in function
1def withdraw(right_pin, enough_cash): 2 # if enough_cash == False: 3 if bool(enough_cash) == False: 4 return 'DENIED' return 'CASH'the test is still green.
I use Logical Negation (NOT) to write the if statement in terms of True
1def withdraw(right_pin, enough_cash): 2 # if enough_cash == False: 3 # if bool(enough_cash) == False: 4 if not bool(enough_cash) == True: 5 return 'DENIED' 6 return 'CASH'still green.
I remove
== True1def withdraw(right_pin, enough_cash): 2 # if enough_cash == False: 3 # if bool(enough_cash) == False: 4 # if not bool(enough_cash) == True: 5 if not bool(enough_cash): 6 return 'DENIED' 7 return 'CASH'green.
I remove bool
1def withdraw(right_pin, enough_cash): 2 # if enough_cash == False: 3 # if bool(enough_cash) == False: 4 # if not bool(enough_cash) == True: 5 # if not bool(enough_cash): 6 if not enough_cash: 7 return 'DENIED' 8 return 'CASH'still green, because
if bool(something) == Falseis the same asif not bool(something) == Trueis the same asif not something.I remove the commented lines from the
withdrawfunction1def withdraw(right_pin, enough_cash): 2 if not enough_cash: 3 return 'DENIED' 4 return 'CASH'I add a git commit message in the other terminal
git commit -am \ 'add test_right_pin_not_enough_cash'
When the withdraw function is called
it returns ‘DENIED’ if there is NOT enough cash in the account
it gives me ‘CASH’ if the above condition is NOT met
withdraw(right_pin=True, enough_cash=False) -> 'DENIED'
withdraw(right_pin=True, enough_cash=True ) -> 'CASH'
What binary operation is the withdraw function using?
test_wrong_pin_enough_cash
RED: make it fail
I go back to the terminal where the tests are running
I add a test with an assertion for when the wrong PIN is entered AND there is enough cash in the account, to
test_atm.pyPIN
cash
withdrawal
wrong PIN
enough cash
DENIED
21 self.assertEqual(reality, my_expectation) 22 23 def test_wrong_pin_enough_cash(self): 24 my_expectation = 'DENIED' 25 reality = src.atm.withdraw( 26 right_pin=False, 27 enough_cash=True, 28 ) 29 self.assertEqual(reality, my_expectation) 30 31 32# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: 'CASH' != 'DENIED'because the
withdrawfunction returned ‘CASH’ and the assertion expects DENIED. Why is this ATM giving me ‘CASH’ when I enter the wrong PIN?
GREEN: make it pass
I add an if statement for this case to atm.py
1def withdraw(right_pin, enough_cash):
2 if right_pin == False:
3 return 'DENIED'
4 if not enough_cash:
5 return 'DENIED'
6 return 'CASH'
the test passes.
REFACTOR: make it better
I add the bool built-in function
1def withdraw(right_pin, enough_cash): 2 # if right_pin == False: 3 if bool(right_pin) == False: 4 return 'DENIED' 5 if not enough_cash: 6 return 'DENIED' 7 return 'CASH'the test is still green.
I use Logical Negation (NOT) to write the if statement in terms of True
1def withdraw(right_pin, enough_cash): 2 # if right_pin == False: 3 # if bool(right_pin) == False: 4 if not bool(right_pin) == True: 5 return 'DENIED' 6 if not enough_cash: 7 return 'DENIED' 8 return 'CASH'still green.
I remove
== True1def withdraw(right_pin, enough_cash): 2 # if right_pin == False: 3 # if bool(right_pin) == False: 4 # if not bool(right_pin) == True: 5 if not bool(right_pin): 6 return 'DENIED' 7 if not enough_cash: 8 return 'DENIED' 9 return 'CASH'green.
I remove bool
1def withdraw(right_pin, enough_cash): 2 # if right_pin == False: 3 # if bool(right_pin) == False: 4 # if not bool(right_pin) == True: 5 # if not bool(right_pin): 6 if not right_pin: 7 return 'DENIED' 8 if not enough_cash: 9 return 'DENIED' 10 return 'CASH'still green, because
if bool(something) == Falseis the same asif not bool(something) == Trueis the same asif not something.I remove the commented lines from the
withdrawfunction1def withdraw(right_pin, enough_cash): 2 if not right_pin: 3 return 'DENIED' 4 if not enough_cash: 5 return 'DENIED' 6 return 'CASH'I add a git commit message in the other terminal
git commit -am \ 'add test_wrong_pin_enough_cash'
When the withdraw function is called
it returns ‘DENIED’ if the wrong PIN is entered.
it returns ‘DENIED’ if there is NOT enough cash in the account.
it gives me ‘CASH’ if the above conditions are NOT met.
withdraw(right_pin=False, enough_cash=True ) -> 'DENIED'
withdraw(right_pin=True , enough_cash=False) -> 'DENIED'
withdraw(right_pin=True , enough_cash=True ) -> 'CASH'
What binary operation is the withdraw function using now?
test_wrong_pin_not_enough_cash
RED: make it fail
I go back to the terminal where the tests are running
I add a test with an assertion for the last case, which is when the wrong PIN is entered AND there is NOT enough cash in the account, to
test_atm.pyPIN
cash
withdrawal
wrong PIN
NOT enough cash
DENIED
29 self.assertEqual(reality, my_expectation) 30 31 def test_wrong_pin_not_enough_cash(self): 32 my_expectation = 'CASH' 33 reality = src.atm.withdraw( 34 right_pin=False, 35 enough_cash=False, 36 ) 37 self.assertEqual(reality, my_expectation) 38 39 40# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: 'DENIED' != 'CASH'
GREEN: make it pass
I change ‘CASH’ to ‘DENIED’ in test_wrong_pin_not_enough_cash
31 def test_wrong_pin_not_enough_cash(self): 32 my_expectation = 'DENIED' 33 reality = src.atm.withdraw( 34 right_pin=False, 35 enough_cash=False, 36 ) 37 self.assertEqual(reality, my_expectation) 38 39 40# Exceptions seenthe test passes.
withdraw(right_pin=False, enough_cash=False) -> 'DENIED' withdraw(right_pin=False, enough_cash=True ) -> 'DENIED' withdraw(right_pin=True , enough_cash=False) -> 'DENIED' withdraw(right_pin=True , enough_cash=True ) -> 'CASH'I add a git commit message in the other terminal
git commit -am \ 'add test_wrong_pin_not_enough_cash'
When the withdraw function is called
it returns ‘DENIED’ if the wrong PIN is entered
withdraw(right_pin=False, enough_cash=False) -> 'DENIED' └── def withdraw(right_pin, enough_cash): └── if not right_pin: └── return 'DENIED' if not enough_cash: return 'DENIED' return 'CASH'withdraw(right_pin=False, enough_cash=True ) -> 'DENIED' └── def withdraw(right_pin, enough_cash): └── if not right_pin: └── return 'DENIED' if not enough_cash: return 'DENIED' return 'CASH'it returns ‘DENIED’ if there is NOT enough cash in the account
withdraw(right_pin=True , enough_cash=False) -> 'DENIED' └── def withdraw(right_pin, enough_cash): ├── if not right_pin: │ return 'DENIED' └── if not enough_cash: └── return 'DENIED' return 'CASH'it only checks if there is enough cash if the right PIN is entered.
it gives me ‘CASH’ if the above conditions are NOT met
withdraw(right_pin=True , enough_cash=True ) -> 'CASH' └── def withdraw(right_pin, enough_cash): ├── if not right_pin: │ return 'DENIED' ├── if not enough_cash: │ return 'DENIED' └── return 'CASH'
extract denied variable
I add a global variable to remove repetition of ‘DENIED’ from the tests because three of them use it, in
test_atm.py1import src.atm 2import unittest 3 4 5DENIED = 'DENIED' 6 7 8class TestATM(unittest.TestCase):I use the new global variable for
my_expectationin test_right_pin_not_enough_cash18 def test_right_pin_not_enough_cash(self): 19 # my_expectation = 'DENIED' 20 reality = src.atm.withdraw( 21 right_pin=True, 22 enough_cash=False, 23 ) 24 # self.assertEqual(reality, my_expectation) 25 self.assertEqual(reality, DENIED) 26 27 def test_wrong_pin_enough_cash(self):the test is still green.
I remove the commented lines from test_right_pin_not_enough_cash
18 def test_right_pin_not_enough_cash(self): 19 reality = src.atm.withdraw( 20 right_pin=True, 21 enough_cash=False, 22 ) 23 self.assertEqual(reality, DENIED) 24 25 def test_wrong_pin_enough_cash(self):I use the new global variable for
my_expectationin test_wrong_pin_enough_cash25 def test_wrong_pin_enough_cash(self): 26 # my_expectation = 'DENIED' 27 reality = src.atm.withdraw( 28 right_pin=False, 29 enough_cash=True, 30 ) 31 # self.assertEqual(reality, my_expectation) 32 self.assertEqual(reality, DENIED) 33 34 def test_wrong_pin_not_enough_cash(self):still green.
I remove the commented lines from test_wrong_pin_enough_cash
25 def test_wrong_pin_enough_cash(self): 26 reality = src.atm.withdraw( 27 right_pin=False, 28 enough_cash=True, 29 ) 30 self.assertEqual(reality, DENIED) 31 32 def test_wrong_pin_not_enough_cash(self):I use the global variable for
my_expectationin test_wrong_pin_not_enough_cash32 def test_wrong_pin_not_enough_cash(self): 33 # my_expectation = 'DENIED' 34 reality = src.atm.withdraw( 35 right_pin=False, 36 enough_cash=False, 37 ) 38 # self.assertEqual(reality, my_expectation) 39 self.assertEqual(reality, DENIED) 40 41 42# Exceptions seengreen.
I remove the commented lines from test_wrong_pin_not_enough_cash
32 def test_wrong_pin_not_enough_cash(self): 33 reality = src.atm.withdraw( 34 right_pin=False, 35 enough_cash=False, 36 ) 37 self.assertEqual(reality, DENIED) 38 39 40# Exceptions seenI add a git commit message in the other terminal
git commit -am \ 'extract denied variable'
So far, the truth table for the Automated Teller Machine is
PIN |
cash |
withdrawal |
|---|---|---|
right PIN |
enough cash |
CASH |
right PIN |
NOT enough cash |
DENIED |
wrong PIN |
enough cash |
DENIED |
wrong PIN |
NOT enough cash |
DENIED |
I want to add a condition for a daily limit on how much can be taken from the account. The inputs to the Automated Teller Machine will then be
is the PIN correct?
is there enough cash in the account for the withdrawal?
will the withdrawal put the account above or below the daily limit for withdrawals?
test_above_limit_right_pin_enough_cash
The truth table for if the right PIN is entered AND there is enough cash in the account, is
PIN |
cash |
daily limit |
withdrawal |
|---|---|---|---|
right PIN |
enough cash |
above limit |
DENIED |
right PIN |
enough cash |
NOT above limit |
CASH |
RED: make it fail
I go back to the terminal where the tests are running
I add a test with an assertion for the case where the right PIN is entered, there is enough cash in the account, and it is above limit for daily withdrawals, to test_right_pin_enough_cash
PIN
cash
daily limit
withdrawal
right PIN
enough cash
above limit
DENIED
10 def test_right_pin_enough_cash(self): 11 my_expectation = 'CASH' 12 reality = src.atm.withdraw( 13 right_pin=True, 14 enough_cash=True, 15 ) 16 self.assertEqual(reality, my_expectation) 17 18 def test_above_limit_right_pin_enough_cash(self): 19 reality = src.atm.withdraw( 20 right_pin=True, 21 enough_cash=True, 22 above_daily_limit=True, 23 ) 24 self.assertEqual(reality, DENIED) 25 26 def test_right_pin_not_enough_cash(self):the terminal is my friend, and shows TypeError
TypeError: withdraw() got an unexpected keyword argument 'above_daily_limit'because the
withdrawfunction got called with a name (above_daily_limit) that is not in the parentheses of its definition.
GREEN: make it pass
I add
above_daily_limitto the function inatm.py1def withdraw( 2 right_pin, enough_cash, 3 above_daily_limit, 4 ): 5 if not right_pin: 6 return 'DENIED' 7 if not enough_cash: 8 return 'DENIED' 9 return 'CASH'the terminal is my friend, and shows AssertionError and TypeError
FAILED ...test_above_limit_right_pin_enough_cash - AssertionError: 'CASH' != 'DENIED' FAILED ...test_right_pin_enough_cash - TypeError: withdraw() missing 1 required positional argument: 'above_daily_limit' FAILED ...test_right_pin_not_enough_cash - TypeError: withdraw() missing 1 required positional argument: 'above_daily_limit' FAILED ...test_wrong_pin_enough_cash - TypeError: withdraw() missing 1 required positional argument: 'above_daily_limit' FAILED ...test_wrong_pin_not_enough_cash - TypeError: withdraw() missing 1 required positional argument: 'above_daily_limit'because
I add a default value for the
above_daily_limitparameter to make it a choice1def withdraw( 2 right_pin, enough_cash, 3 above_daily_limit=False, 4 ):the terminal is my friend, and shows AssertionError
AssertionError: 'CASH' != 'DENIED'because the
withdrawfunction returns ‘CASH’ and the assertion expects ‘DENIED’.I add an if statement for this case
1def withdraw( 2 right_pin, enough_cash, 3 above_daily_limit=False, 4 ): 5 if above_daily_limit == True: 6 return 'DENIED' 7 if not right_pin: 8 return 'DENIED' 9 if not enough_cash: 10 return 'DENIED' 11 return 'CASH'the test passes.
REFACTOR: make it better
I remove
== True1def withdraw( 2 right_pin, enough_cash, 3 above_daily_limit=False, 4 ): 5 # if above_daily_limit == True: 6 if above_daily_limit: 7 return 'DENIED'the test is still green because
if something == Trueis the same asif bool(something) == Trueis the same asif bool(something)is the same asif something.I add a variable for
'DENIED'1def withdraw( 2 right_pin, enough_cash, 3 above_daily_limit=False, 4 ): 5 denied = 'DENIED' 6 # if above_daily_limit == True:I use the variable to remove repetition of
'DENIED'1def withdraw( 2 right_pin, enough_cash, 3 above_daily_limit=False, 4 ): 5 denied = 'DENIED' 6 # if above_daily_limit == True: 7 if above_daily_limit: 8 # return 'DENIED' 9 return denied 10 if not right_pin: 11 # return 'DENIED' 12 return denied 13 if not enough_cash: 14 # return 'DENIED' 15 return denied 16 return 'CASH'still green.
I remove the commented lines from the
withdrawfunction1def withdraw( 2 right_pin, enough_cash, 3 above_daily_limit=False, 4 ): 5 denied = 'DENIED' 6 if above_daily_limit: 7 return denied 8 if not right_pin: 9 return denied 10 if not enough_cash: 11 return denied 12 return 'CASH'I add a git commit message in the other terminal
git commit -am \ 'add test_above_limit_right_pin_enough_cash'
When the withdraw function is called
it returns ‘DENIED’ if the account is above limit for daily withdrawals.
it returns ‘DENIED’ if the wrong PIN is entered.
it returns ‘DENIED’ if there is NOT enough cash in the account.
it gives me ‘CASH’ if the above conditions are NOT met.
withdraw(
right_pin=True, enough_cash=True,
above_daily_limit=True
) -> 'DENIED'
test_below_limit_right_pin_enough_cash
The truth table for if the right PIN is entered AND there is enough cash in the account, is
PIN |
cash |
daily limit |
withdrawal |
|---|---|---|---|
right PIN |
enough cash |
above limit |
DENIED |
right PIN |
enough cash |
NOT above limit |
CASH |
I go back to the terminal where the tests are running
I do not need to add anything to test_right_pin_enough_cash which is for when the right PIN is entered, AND there is enough cash in the account, and it is NOT above limit for daily withdrawals, because the default value for the
above_daily_limitparameter of thewithdrawfunction is FalsePIN
cash
daily limit
withdrawal
right PIN
enough cash
NOT above limit
CASH
this means that
src.atm.withdraw( right_pin=True, enough_cash=True, )is the same as
src.atm.withdraw( right_pin=True, enough_cash=True, above_daily_limit=False, )because a function uses the default value for a parameter when it is called without the parameter.
I change the name of the test from test_right_pin_enough_cash to test_below_limit_right_pin_enough_cash
8class TestATM(unittest.TestCase): 9 10 def test_below_limit_right_pin_enough_cash(self): 11 my_expectation = 'CASH' 12 reality = src.atm.withdraw( 13 right_pin=True, 14 enough_cash=True, 15 ) 16 self.assertEqual(reality, my_expectation) 17 18 def test_above_limit_right_pin_enough_cash(self):I add a git commit message in the other terminal
git commit -am \ 'add test_below_limit_right_pin_enough_cash'
When the withdraw function is called
it returns ‘DENIED’ if the account is above limit for daily withdrawals.
it returns ‘DENIED’ if the wrong PIN is entered.
it returns ‘DENIED’ if there is NOT enough cash in the account.
it gives me ‘CASH’ if the above conditions are NOT met.
withdraw(
right_pin=True, enough_cash=True,
above_daily_limit=True
) -> 'DENIED'
withdraw(
right_pin=True, enough_cash=True,
above_daily_limit=False
) -> 'CASH'
test_above_limit_right_pin_not_enough_cash
The truth table for if the right PIN is entered AND there is NOT enough cash in the account, is
PIN |
cash |
daily limit |
withdrawal |
|---|---|---|---|
right PIN |
NOT enough cash |
above limit |
DENIED |
right PIN |
NOT enough cash |
NOT above limit |
DENIED |
I go back to the terminal where the tests are running
I add a value for the
above_daily_limitparameter to the assertion in test_right_pin_not_enough_cash, for the case where the right PIN is entered, there is NOT enough cash in the account, and it is above limit for daily withdrawals,PIN
cash
daily limit
withdrawal
right PIN
NOT enough cash
above limit
DENIED
26 def test_right_pin_not_enough_cash(self): 27 reality = src.atm.withdraw( 28 right_pin=True, 29 enough_cash=False, 30 above_daily_limit=True, 31 ) 32 self.assertEqual(reality, DENIED) 33 34 def test_wrong_pin_enough_cash(self):the test is still green.
I change the name of the test from test_right_pin_not_enough_cash to test_above_limit_right_pin_not_enough_cash
24 self.assertEqual(reality, DENIED) 25 26 def test_above_limit_right_pin_not_enough_cash(self): 27 reality = src.atm.withdraw( 28 right_pin=True, 29 enough_cash=False, 30 above_daily_limit=True, 31 ) 32 self.assertEqual(reality, DENIED) 33 34 def test_wrong_pin_enough_cash(self):I add a git commit message in the other terminal
git commit -am \ 'add test_above_limit_right_pin_not_enough_cash'
When the withdraw function is called
it returns ‘DENIED’ if the account is above limit for daily withdrawals.
it returns ‘DENIED’ if the wrong PIN is entered.
it returns ‘DENIED’ if there is NOT enough cash in the account.
it gives me ‘CASH’ if the above conditions are NOT met.
withdraw(
right_pin=True, enough_cash=False,
above_daily_limit=True
) -> 'DENIED'
withdraw(
right_pin=True, enough_cash=True,
above_daily_limit=True
) -> 'DENIED'
withdraw(
right_pin=True, enough_cash=True,
above_daily_limit=False
) -> 'CASH'
test_below_limit_right_pin_not_enough_cash
The truth table for if the right PIN is entered AND there is NOT enough cash in the account, is
PIN |
cash |
daily limit |
withdrawal |
|---|---|---|---|
right PIN |
NOT enough cash |
above limit |
DENIED |
right PIN |
NOT enough cash |
NOT above limit |
DENIED |
RED: make it fail
I go back to the terminal where the tests are running
I add a test with an assertion for when the right PIN is entered, there is NOT enough cash in the account, and it is NOT above limit for daily withdrawals
PIN
cash
daily limit
withdrawal
right PIN
NOT enough cash
NOT above limit
DENIED
32 self.assertEqual(reality, DENIED) 33 34 def test_below_limit_right_pin_not_enough_cash(self): 35 reality = src.atm.withdraw( 36 right_pin=True, 37 enough_cash=False, 38 ) 39 self.assertEqual(reality, 'CASH') 40 41 def test_wrong_pin_enough_cash(self):the terminal is my friend, and shows AssertionError
AssertionError: 'DENIED' != 'CASH'
GREEN: make it pass
I change my expectation to match reality in test_below_limit_right_pin_not_enough_cash
34 def test_below_limit_right_pin_not_enough_cash(self): 35 reality = src.atm.withdraw( 36 right_pin=True, 37 enough_cash=False, 38 ) 39 self.assertEqual(reality, DENIED) 40 41 def test_wrong_pin_enough_cash(self):the test passes. I do not need to give a value for the
above_daily_limitparameter because the default value for theabove_daily_limitparameter of thewithdrawfunction is False. This means thatsrc.atm.withdraw( right_pin=True, enough_cash=False, )is the same as
src.atm.withdraw( right_pin=True, enough_cash=False, above_daily_limit=False, )because a function uses the default value for a parameter when it is called without the parameter.
I add a git commit message in the other terminal
git commit -am \ 'add test_below_limit_right_pin_not_enough_cash'
When the withdraw function is called
it returns ‘DENIED’ if the account is above limit for daily withdrawals.
it returns ‘DENIED’ if the wrong PIN is entered.
it returns ‘DENIED’ if there is NOT enough cash in the account.
it gives me ‘CASH’ if the above conditions are NOT met.
withdraw(
right_pin=True, enough_cash=False,
above_daily_limit=False
) -> 'DENIED'
withdraw(
right_pin=True, enough_cash=False,
above_daily_limit=True
) -> 'DENIED'
withdraw(
right_pin=True, enough_cash=True,
above_daily_limit=True
) -> 'DENIED'
withdraw(
right_pin=True, enough_cash=True,
above_daily_limit=False
) -> 'CASH'
test_above_limit_wrong_pin_enough_cash
The truth table for if the wrong PIN is entered AND there is enough cash in the account, is
PIN |
cash |
daily limit |
withdrawal |
|---|---|---|---|
wrong PIN |
enough cash |
above limit |
DENIED |
wrong PIN |
enough cash |
NOT above limit |
DENIED |
I go back to the terminal where the tests are running
I add a value for the
above_daily_limitparameter to the assertion in test_wrong_pin_enough_cash, for the case where the wrong PIN is entered, there is enough cash in the account, and it is above limit for daily withdrawalsPIN
cash
daily limit
withdrawal
wrong PIN
enough cash
above limit
DENIED
41 def test_wrong_pin_enough_cash(self): 42 reality = src.atm.withdraw( 43 right_pin=False, 44 enough_cash=True, 45 above_daily_limit=True, 46 ) 47 self.assertEqual(reality, DENIED) 48 49 def test_wrong_pin_not_enough_cash(self):the test is still green.
I change the name of test_wrong_pin_enough_cash to test_above_limit_wrong_pin_enough_cash
39 self.assertEqual(reality, DENIED) 40 41 def test_above_limit_wrong_pin_enough_cash(self): 42 reality = src.atm.withdraw( 43 right_pin=False, 44 enough_cash=True, 45 above_daily_limit=True, 46 )I add a git commit message in the other terminal
git commit -am \ 'add test_above_limit_wrong_pin_enough_cash'
When the withdraw function is called
it returns ‘DENIED’ if the account is above limit for daily withdrawals.
it returns ‘DENIED’ if the wrong PIN is entered.
it returns ‘DENIED’ if there is NOT enough cash in the account.
it gives me ‘CASH’ if the above conditions are NOT met.
withdraw(
right_pin=False, enough_cash=True,
above_daily_limit=True
) -> 'DENIED'
test_below_limit_wrong_pin_enough_cash
The truth table for if the wrong PIN is entered AND there is enough cash in the account, is
PIN |
cash |
daily limit |
withdrawal |
|---|---|---|---|
wrong PIN |
enough cash |
above limit |
DENIED |
wrong PIN |
enough cash |
NOT above limit |
DENIED |
RED: make it fail
I go back to the terminal where the tests are running
I add a test with an assertion for when the wrong PIN is entered, there is enough cash in the account, and it is NOT above limit for daily withdrawals
PIN
cash
daily limit
withdrawal
wrong PIN
enough cash
NOT above limit
DENIED
47 self.assertEqual(reality, DENIED) 48 49 def test_below_limit_wrong_pin_enough_cash(self): 50 reality = src.atm.withdraw( 51 right_pin=False, 52 enough_cash=True, 53 ) 54 self.assertEqual(reality, 'CASH') 55 56 def test_wrong_pin_not_enough_cash(self):the terminal is my friend, and shows AssertionError
AssertionError: 'DENIED' != 'CASH'
GREEN: make it pass
I change ‘CASH’ to DENIED in test_below_limit_wrong_pin_enough_cash
49 def test_below_limit_wrong_pin_enough_cash(self): 50 reality = src.atm.withdraw( 51 right_pin=False, 52 enough_cash=True, 53 ) 54 self.assertEqual(reality, DENIED) 55 56 def test_wrong_pin_not_enough_cash(self):the test passes. I do not need to give a value for the
above_daily_limitparameter in the call tosrc.atm.withdrawbecause the default value for theabove_daily_limitparameter of thewithdrawfunction is False. This means thatsrc.atm.withdraw( right_pin=False, enough_cash=True, )is the same as
src.atm.withdraw( right_pin=False, enough_cash=True, above_daily_limit=False, )because a function uses the default value for a parameter when it is called without the parameter.
I add a git commit message in the other terminal
git commit -am \ 'add test_below_limit_wrong_pin_enough_cash'
When the withdraw function is called
it returns ‘DENIED’ if the account is above limit for daily withdrawals.
it returns ‘DENIED’ if the wrong PIN is entered.
it returns ‘DENIED’ if there is NOT enough cash in the account.
it gives me ‘CASH’ if the above conditions are NOT met.
withdraw(
right_pin=False, enough_cash=True,
above_daily_limit=False
) -> 'DENIED'
withdraw(
right_pin=False, enough_cash=True,
above_daily_limit=True
) -> 'DENIED'
test_above_limit_wrong_pin_not_enough_cash
The truth table for if the wrong PIN is entered AND there is NOT enough cash in the account, is
PIN |
cash |
daily limit |
withdrawal |
|---|---|---|---|
wrong PIN |
NOT enough cash |
above limit |
DENIED |
wrong PIN |
NOT enough cash |
NOT above limit |
DENIED |
I go back to the terminal where the tests are running
I add
above_daily_limitto the call tosrc.atm.withdrawfrom test_wrong_pin_not_enough_cash, for when the wrong PIN is entered, there is NOT enough cash in the account, and it is above limit for daily withdrawalsPIN
cash
daily limit
withdrawal
wrong PIN
NOT enough cash
above limit
DENIED
56 def test_wrong_pin_not_enough_cash(self): 57 reality = src.atm.withdraw( 58 right_pin=False, 59 enough_cash=False, 60 above_daily_limit=True, 61 ) 62 self.assertEqual(reality, DENIED) 63 64 65# Exceptions seenthe test is still green.
I change the name of the test from test_wrong_pin_not_enough_cash to test_above_limit_wrong_pin_not_enough_cash
54 self.assertEqual(reality, DENIED) 55 56 def test_above_limit_wrong_pin_not_enough_cash(self): 57 reality = src.atm.withdraw( 58 right_pin=False, 59 enough_cash=False, 60 above_daily_limit=True, 61 )I add a git commit message in the other terminal
git commit -am \ 'add test_above_limit_wrong_pin_not_enough_cash'
When the withdraw function is called
it returns ‘DENIED’ if the account is above limit for daily withdrawals.
it returns ‘DENIED’ if the wrong PIN is entered.
it returns ‘DENIED’ if there is NOT enough cash in the account.
it gives me ‘CASH’ if the above conditions are NOT met.
withdraw(
right_pin=False, enough_cash=False,
above_daily_limit=True
) -> 'DENIED'
withdraw(
right_pin=False, enough_cash=True,
above_daily_limit=False
) -> 'DENIED'
withdraw(
right_pin=False, enough_cash=True,
above_daily_limit=True
) -> 'DENIED'
test_below_limit_wrong_pin_not_enough_cash
The truth table for if the wrong PIN is entered AND there is NOT enough cash in the account, is
PIN |
cash |
daily limit |
withdrawal |
|---|---|---|---|
wrong PIN |
NOT enough cash |
above limit |
DENIED |
wrong PIN |
NOT enough cash |
NOT above limit |
DENIED |
RED: make it fail
I go back to the terminal where the tests are running
I add a test with an assertion for when the wrong PIN is entered, there is NOT enough cash in the account, and it is NOT above limit for daily withdrawals
PIN
cash
daily limit
withdrawal
wrong PIN
NOT enough cash
NOT above limit
DENIED
62 self.assertEqual(reality, DENIED) 63 64 def test_below_limit_wrong_pin_not_enough_cash(self): 65 reality = src.atm.withdraw( 66 right_pin=False, 67 enough_cash=False, 68 ) 69 self.assertEqual(reality, 'CASH') 70 71 72# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: 'DENIED' != 'CASH'
GREEN: make it pass
I change ‘CASH’ to DENIED in test_below_limit_wrong_pin_not_enough_cash
64 def test_below_limit_wrong_pin_not_enough_cash(self): 65 reality = src.atm.withdraw( 66 right_pin=False, 67 enough_cash=False, 68 ) 69 self.assertEqual(reality, DENIED) 70 71 72# Exceptions seenthe test passes.
withdraw( right_pin=False, enough_cash=False, above_daily_limit=False ) -> 'DENIED' withdraw( right_pin=False, enough_cash=False, above_daily_limit=True ) -> 'DENIED' withdraw( right_pin=False, enough_cash=True, above_daily_limit=False ) -> 'DENIED' withdraw( right_pin=False, enough_cash=True, above_daily_limit=True ) -> 'DENIED'I do not need to give a value for the
above_daily_limitparameter in the call tosrc.atm.withdrawbecause the default value for theabove_daily_limitparameter of thewithdrawfunction is False. This means thatreality = src.atm.withdraw( right_pin=False, enough_cash=False, )is the same as
reality = src.atm.withdraw( right_pin=False, enough_cash=False, above_daily_limit=False, )because a function uses the default value for a parameter when it is called without the parameter.
I add a git commit message in the other terminal
git commit -am \ 'add test_below_limit_wrong_pin_not_enough_cash'
When the withdraw function is called
it returns ‘DENIED’ if the account is above limit for daily withdrawals.
withdraw( right_pin=False, enough_cash=False, above_daily_limit=True ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, ): ├── denied = 'DENIED' └── if above_daily_limit: └── return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=False, enough_cash=True, above_daily_limit=True ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, ): ├── denied = 'DENIED' └── if above_daily_limit: └── return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=True, enough_cash=True, above_daily_limit=True ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, ): ├── denied = 'DENIED' └── if above_daily_limit: └── return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'it returns ‘DENIED’ if the wrong PIN is entered.
withdraw( right_pin=False, enough_cash=False, above_daily_limit=False ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, ): ├── denied = 'DENIED' ├── if above_daily_limit: │ return denied └── if not right_pin: └── return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=False, enough_cash=True, above_daily_limit=False ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, ): ├── denied = 'DENIED' ├── if above_daily_limit: │ return denied └── if not right_pin: └── return denied if not enough_cash: return denied return 'CASH'it only checks if the right PIN is entered if the account is NOT above limit for daily withdrawals.
it returns ‘DENIED’ if there is NOT enough cash in the account.
withdraw( right_pin=True, enough_cash=False, above_daily_limit=False ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, ): ├── denied = 'DENIED' ├── if above_daily_limit: │ return denied ├── if not right_pin: │ return denied └── if not enough_cash: └── return denied return 'CASH'withdraw( right_pin=True, enough_cash=False, above_daily_limit=True ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, ): ├── denied = 'DENIED' ├── if above_daily_limit: │ return denied ├── if not right_pin: │ return denied └── if not enough_cash: └── return denied return 'CASH'it only checks if there is enough cash in the account if the right PIN is entered.
it gives me ‘CASH’ if the above conditions are NOT met.
withdraw( right_pin=True, enough_cash=True, above_daily_limit=False ) -> 'CASH' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, ): ├── denied = 'DENIED' ├── if above_daily_limit: │ return denied ├── if not right_pin: │ return denied ├── if not enough_cash: │ return denied └── return 'CASH'
remove reality variable
I can call the withdraw function directly in all the assertions of the tests. I do not need the reality variable as a middle man because I only use the variable once in each test.
I call
src.atm.withdrawdirectly in the assertion of test_below_limit_wrong_pin_not_enough_cash64 def test_below_limit_wrong_pin_not_enough_cash(self): 65 # reality = src.atm.withdraw( 66 # right_pin=False, 67 # enough_cash=False, 68 # ) 69 # self.assertEqual(reality, DENIED) 70 self.assertEqual( 71 src.atm.withdraw( 72 right_pin=False, 73 enough_cash=False, 74 ), 75 DENIED 76 ) 77 78 79# Exceptions seenthe test is still green.
I remove the commented lines from test_below_limit_wrong_pin_not_enough_cash
64 def test_below_limit_wrong_pin_not_enough_cash(self): 65 self.assertEqual( 66 src.atm.withdraw( 67 right_pin=False, 68 enough_cash=False, 69 ), 70 DENIED 71 ) 72 73 74# Exceptions seenI call
src.atm.withdrawdirectly in the assertion of test_above_limit_wrong_pin_not_enough_cash56 def test_above_limit_wrong_pin_not_enough_cash(self): 57 # reality = src.atm.withdraw( 58 # right_pin=False, 59 # enough_cash=False, 60 # above_daily_limit=True, 61 # ) 62 # self.assertEqual(reality, DENIED) 63 self.assertEqual( 64 src.atm.withdraw( 65 right_pin=False, 66 enough_cash=False, 67 above_daily_limit=True, 68 ), 69 DENIED 70 ) 71 72 def test_below_limit_wrong_pin_not_enough_cash(self):still green.
I remove the commented lines from test_above_limit_wrong_pin_not_enough_cash
56 def test_above_limit_wrong_pin_not_enough_cash(self): 57 self.assertEqual( 58 src.atm.withdraw( 59 right_pin=False, 60 enough_cash=False, 61 above_daily_limit=True, 62 ), 63 DENIED 64 ) 65 66 def test_below_limit_wrong_pin_not_enough_cash(self):I call
src.atm.withdrawdirectly in the assertion of test_below_limit_wrong_pin_enough_cash49 def test_below_limit_wrong_pin_enough_cash(self): 50 # reality = src.atm.withdraw( 51 # right_pin=False, 52 # enough_cash=True, 53 # ) 54 # self.assertEqual(reality, DENIED) 55 self.assertEqual( 56 src.atm.withdraw( 57 right_pin=False, 58 enough_cash=True, 59 ), 60 DENIED 61 ) 62 63 def test_above_limit_wrong_pin_not_enough_cash(self):green.
I remove the commented lines from test_below_limit_wrong_pin_enough_cash
49 def test_below_limit_wrong_pin_enough_cash(self): 50 self.assertEqual( 51 src.atm.withdraw( 52 right_pin=False, 53 enough_cash=True, 54 ), 55 DENIED 56 ) 57 58 def test_above_limit_wrong_pin_not_enough_cash(self):I call
src.atm.withdrawdirectly in the assertion of test_above_limit_wrong_pin_enough_cash41 def test_above_limit_wrong_pin_enough_cash(self): 42 # reality = src.atm.withdraw( 43 # right_pin=False, 44 # enough_cash=True, 45 # above_daily_limit=True, 46 # ) 47 # self.assertEqual(reality, DENIED) 48 self.assertEqual( 49 src.atm.withdraw( 50 right_pin=False, 51 enough_cash=True, 52 above_daily_limit=True, 53 ), 54 DENIED 55 ) 56 57 def test_below_limit_wrong_pin_enough_cash(self):still green.
I remove the commented lines from test_above_limit_wrong_pin_enough_cash
41 def test_above_limit_wrong_pin_enough_cash(self): 42 self.assertEqual( 43 src.atm.withdraw( 44 right_pin=False, 45 enough_cash=True, 46 above_daily_limit=True, 47 ), 48 DENIED 49 ) 50 51 def test_below_limit_wrong_pin_enough_cash(self):I call
src.atm.withdrawdirectly in the assertion of test_below_limit_right_pin_not_enough_cash34 def test_below_limit_right_pin_not_enough_cash(self): 35 # reality = src.atm.withdraw( 36 # right_pin=True, 37 # enough_cash=False, 38 # ) 39 # self.assertEqual(reality, DENIED) 40 self.assertEqual( 41 src.atm.withdraw( 42 right_pin=True, 43 enough_cash=False, 44 ), 45 DENIED 46 ) 47 48 def test_above_limit_wrong_pin_enough_cash(self):the test is still green.
I remove the commented lines from test_below_limit_right_pin_not_enough_cash
34 def test_below_limit_right_pin_not_enough_cash(self): 35 self.assertEqual( 36 src.atm.withdraw( 37 right_pin=True, 38 enough_cash=False, 39 ), 40 DENIED 41 ) 42 43 def test_above_limit_wrong_pin_enough_cash(self):I call
src.atm.withdrawdirectly in the assertion of test_above_limit_right_pin_not_enough_cash26 def test_above_limit_right_pin_not_enough_cash(self): 27 # reality = src.atm.withdraw( 28 # right_pin=True, 29 # enough_cash=False, 30 # above_daily_limit=True, 31 # ) 32 # self.assertEqual(reality, DENIED) 33 self.assertEqual( 34 src.atm.withdraw( 35 right_pin=True, 36 enough_cash=False, 37 above_daily_limit=True, 38 ), 39 DENIED 40 ) 41 42 def test_below_limit_right_pin_not_enough_cash(self):still green.
I remove the commented lines from test_above_limit_right_pin_not_enough_cash
26 def test_above_limit_right_pin_not_enough_cash(self): 27 self.assertEqual( 28 src.atm.withdraw( 29 right_pin=True, 30 enough_cash=False, 31 above_daily_limit=True, 32 ), 33 DENIED 34 ) 35 36 def test_below_limit_right_pin_not_enough_cash(self):I call
src.atm.withdrawdirectly in the assertion of test_above_limit_right_pin_enough_cash18 def test_above_limit_right_pin_enough_cash(self): 19 # reality = src.atm.withdraw( 20 # right_pin=True, 21 # enough_cash=True, 22 # above_daily_limit=True, 23 # ) 24 # self.assertEqual(reality, DENIED) 25 self.assertEqual( 26 src.atm.withdraw( 27 right_pin=True, 28 enough_cash=True, 29 above_daily_limit=True, 30 ), 31 DENIED 32 ) 33 34 def test_above_limit_right_pin_not_enough_cash(self):still green.
I remove the commented lines from test_above_limit_right_pin_enough_cash
18 def test_above_limit_right_pin_enough_cash(self): 19 self.assertEqual( 20 src.atm.withdraw( 21 right_pin=True, 22 enough_cash=True, 23 above_daily_limit=True, 24 ), 25 DENIED 26 ) 27 28 def test_above_limit_right_pin_not_enough_cash(self):I call
src.atm.withdrawdirectly and use ‘CASH’ formy_expectationin the assertion of test_below_limit_right_pin_not_enough_cash10 def test_below_limit_right_pin_enough_cash(self): 11 # my_expectation = 'CASH' 12 # reality = src.atm.withdraw( 13 # right_pin=True, 14 # enough_cash=True, 15 # ) 16 self.assertEqual( 17 src.atm.withdraw( 18 right_pin=True, 19 enough_cash=True, 20 ), 21 'CASH' 22 ) 23 24 def test_above_limit_right_pin_enough_cash(self):the test is still green.
I remove the commented lines from test_below_limit_right_pin_not_enough_cash
10 def test_below_limit_right_pin_enough_cash(self): 11 self.assertEqual( 12 src.atm.withdraw( 13 right_pin=True, 14 enough_cash=True, 15 ), 16 'CASH' 17 ) 18 19 def test_above_limit_right_pin_enough_cash(self):I add a git commit message in the other terminal
git commit -am 'remove reality variable'
The truth table for the Automated Teller Machine is now
PIN |
cash |
daily limit |
withdrawal |
|---|---|---|---|
right PIN |
enough cash |
above limit |
DENIED |
right PIN |
enough cash |
NOT above limit |
CASH |
right PIN |
NOT enough cash |
above limit |
DENIED |
right PIN |
NOT enough cash |
NOT above limit |
DENIED |
PIN |
cash |
daily limit |
withdrawal |
|---|---|---|---|
wrong PIN |
enough cash |
above limit |
DENIED |
wrong PIN |
enough cash |
NOT above limit |
DENIED |
wrong PIN |
NOT enough cash |
above limit |
DENIED |
wrong PIN |
NOT enough cash |
NOT above limit |
DENIED |
What if the bank card has expired? The inputs to the Automated Teller Machine will then be
has the card expired?
is the PIN correct?
is there enough cash in the account for the withdrawal?
will the withdrawal put the account above or below the daily limit for withdrawals?
test_card_w_below_limit_right_pin_enough_cash
The truth table for if the right PIN is entered, AND there is enough cash in the account, AND it is NOT above limit for daily withdrawals is
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
enough cash |
NOT above limit |
expired |
DENIED |
right PIN |
enough cash |
NOT above limit |
NOT expired |
CASH |
RED: make it fail
I go back to the terminal where the tests are running
I add a value for the
card_expiredparameter to the call to thewithdrawfunction for the case where the right PIN is entered, there is enough cash in the account, it is NOT above limit for daily withdrawals, and the card has NOT expired, in test_below_limit_right_pin_enough_cashPIN
cash
daily limit
card expired
withdrawal
right PIN
enough cash
NOT above limit
NOT expired
CASH
10 def test_below_limit_right_pin_enough_cash(self): 11 self.assertEqual( 12 src.atm.withdraw( 13 right_pin=True, 14 enough_cash=True, 15 card_expired=False, 16 ), 17 'CASH' 18 ) 19 20 def test_above_limit_right_pin_enough_cash(self):the terminal is my friend, and shows TypeError
TypeError: withdraw() got an unexpected keyword argument 'card_expired'because the test called the
withdrawfunction with a name (card_expired) that is not in the parentheses of its definition.
GREEN: make it pass
I add
card_expiredto the function signature inatm.py1def withdraw( 2 right_pin, enough_cash, 3 above_daily_limit=False, card_expired, 4 ):the terminal is my friend, and shows SyntaxError
SyntaxError: parameter without a default follows parameter with a defaultbecause parameters without default values must come before parameters with default values.
I add SyntaxError to the list of Exceptions seen, in
test_atm.py88# Exceptions seen 89# AssertionError 90# NameError 91# AttributeError 92# TypeError 93# SyntaxErrorI add a default value for
card_expiredinatm.py1def withdraw( 2 right_pin, enough_cash, 3 above_daily_limit=False, card_expired=False, 4 ):the test passes.
withdraw( right_pin=True, enough_cash=True, above_daily_limit=False, card_expired=False ) -> 'CASH'
REFACTOR: make it better
I add the value for the
above_daily_limitparameter to test_below_limit_right_pin_enough_cash to make the assertion clearer, intest_atm.py10 def test_below_limit_right_pin_enough_cash(self): 11 self.assertEqual( 12 src.atm.withdraw( 13 right_pin=True, 14 enough_cash=True, 15 above_daily_limit=False, 16 card_expired=False, 17 ), 18 'CASH' 19 ) 20 21 def test_above_limit_right_pin_enough_cash(self):the test is still green.
I add an assertion for when the right PIN is entered, there is enough cash in the account, it is NOT above limit for daily withdrawals, and the card has expired
PIN
cash
daily limit
card expired
withdrawal
right PIN
enough cash
NOT above limit
expired
DENIED
10 def test_below_limit_right_pin_enough_cash(self): 11 self.assertEqual( 12 src.atm.withdraw( 13 right_pin=True, 14 enough_cash=True, 15 above_daily_limit=False, 16 card_expired=False, 17 ), 18 'CASH' 19 ) 20 self.assertEqual( 21 src.atm.withdraw( 22 right_pin=True, 23 enough_cash=True, 24 above_daily_limit=False, 25 card_expired=True, 26 ), 27 DENIED 28 ) 29 30 def test_above_limit_right_pin_enough_cash(self):the terminal is my friend, and shows AssertionError
AssertionError: 'CASH' != 'DENIED'The ATM should not give me CASH if the bank card has expired.
I add an if statement to the
withdrawfunction inatm.py1def withdraw( 2 right_pin, enough_cash, 3 above_daily_limit=False, card_expired=False, 4 ): 5 denied = 'DENIED' 6 if card_expired == True: 7 return denied 8 if above_daily_limit: 9 return deniedthe test passes.
withdraw( right_pin=True, enough_cash=True, above_daily_limit=False, card_expired=True ) -> 'DENIED' withdraw( right_pin=True, enough_cash=True, above_daily_limit=False, card_expired=False ) -> 'CASH'I remove
== True1def withdraw( 2 right_pin, enough_cash, 3 above_daily_limit=False, card_expired=False, 4 ): 5 denied = 'DENIED' 6 if card_expired: 7 return denied 8 if above_daily_limit: 9 return denied 10 if not right_pin: 11 return denied 12 if not enough_cash: 13 return denied 14 return 'CASH'the test is still green.
I change the name of the test from test_below_limit_right_pin_enough_cash to test_card_w_below_limit_right_pin_enough_cash in
test_atm.py8class TestATM(unittest.TestCase): 9 10 def test_card_w_below_limit_right_pin_enough_cash(self): 11 self.assertEqual( 12 src.atm.withdraw( 13 right_pin=True, 14 enough_cash=True, 15 above_daily_limit=False, 16 card_expired=False, 17 ), 18 'CASH' 19 )I add a git commit message in the other terminal
git commit -am \ 'add test_card_w_below_limit_right_pin_enough_cash'
When the withdraw function is called
it returns ‘DENIED’ if the card has expired.
it returns ‘DENIED’ if the account is above limit for daily withdrawals.
it returns ‘DENIED’ if the wrong PIN is entered.
it returns ‘DENIED’ if there is NOT enough cash in the account.
it gives me ‘CASH’ if the above conditions are NOT met.
test_card_w_above_limit_right_pin_enough_cash
The truth table for if the right PIN is entered, AND there is enough cash in the account, AND it is above limit for daily withdrawals is
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
enough cash |
above limit |
expired |
DENIED |
right PIN |
enough cash |
above limit |
NOT expired |
DENIED |
RED: make it fail
I go back to the terminal where the tests are running
I add a value for the
card_expiredparameter to the call to thewithdrawfunction for the case where the right PIN is entered, there is enough cash in the account, it is above limit for daily withdrawals, and the card has expired, in test_above_limit_right_pin_enough_cashPIN
cash
daily limit
card expired
withdrawal
right PIN
enough cash
above limit
expired
DENIED
30 def test_above_limit_right_pin_enough_cash(self): 31 self.assertEqual( 32 src.atm.withdraw( 33 right_pin=True, 34 enough_cash=True, 35 above_daily_limit=True, 36 card_expired=True, 37 ), 38 DENIED 39 ) 40 41 def test_above_limit_right_pin_not_enough_cash(self):the test is still green.
withdraw( right_pin=True, enough_cash=True, above_daily_limit=True, card_expired=True ) -> 'DENIED'I add an assertion for the case where the right PIN is entered, there is enough cash in the account, it is above limit for daily withdrawals, and the card has NOT expired
PIN
cash
daily limit
card expired
withdrawal
right PIN
enough cash
above limit
NOT expired
DENIED
30 def test_above_limit_right_pin_enough_cash(self): 31 self.assertEqual( 32 src.atm.withdraw( 33 right_pin=True, 34 enough_cash=True, 35 above_daily_limit=True, 36 card_expired=True, 37 ), 38 DENIED 39 ) 40 self.assertEqual( 41 src.atm.withdraw( 42 right_pin=True, 43 enough_cash=True, 44 above_daily_limit=True, 45 card_expired=False, 46 ), 47 'CASH' 48 ) 49 50 def test_above_limit_right_pin_not_enough_cash(self):the terminal is my friend, and shows AssertionError
AssertionError: 'DENIED' != 'CASH'
GREEN: make it pass
I change ‘CASH’ to DENIED in test_above_limit_right_pin_enough_cash
41 self.assertEqual( 42 src.atm.withdraw( 43 right_pin=True, 44 enough_cash=True, 45 above_daily_limit=True, 46 card_expired=False, 47 ), 48 DENIED 49 ) 50 51 def test_above_limit_right_pin_not_enough_cash(self):the test passes.
withdraw( right_pin=True, enough_cash=True, above_daily_limit=True, card_expired=False ) -> 'DENIED' withdraw( right_pin=True, enough_cash=True, above_daily_limit=True, card_expired=True ) -> 'DENIED'
REFACTOR: make it better
I change the name of the test from test_above_limit_right_pin_enough_cash to test_card_w_above_limit_right_pin_enough_cash
20 self.assertEqual( 21 src.atm.withdraw( 22 right_pin=True, 23 enough_cash=True, 24 above_daily_limit=False, 25 card_expired=True, 26 ), 27 DENIED 28 ) 29 30 def test_card_w_above_limit_right_pin_enough_cash(self): 31 self.assertEqual( 32 src.atm.withdraw( 33 right_pin=True, 34 enough_cash=True, 35 above_daily_limit=True, 36 card_expired=True, 37 ), 38 DENIED 39 )I add a git commit message in the other terminal
git commit -am \ 'add test_card_w_above_limit_right_pin_enough_cash'
test_card_w_above_limit_right_pin_not_enough_cash
The truth table for if the right PIN is entered AND there is NOT enough cash in the account, AND it is above limit for daily withdrawals is
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
NOT enough cash |
above limit |
expired |
DENIED |
right PIN |
NOT enough cash |
above limit |
NOT expired |
DENIED |
RED: make it fail
I go back to the terminal where the tests are running
I add a value for the
card_expiredparameter to the call to thewithdrawfunction for the case where the right PIN is entered, there is NOT enough cash in the account, it is above limit for daily withdrawals, and the card has expiredPIN
cash
daily limit
card expired
withdrawal
right PIN
NOT enough cash
above limit
expired
DENIED
50 def test_above_limit_right_pin_not_enough_cash(self): 51 self.assertEqual( 52 src.atm.withdraw( 53 right_pin=True, 54 enough_cash=False, 55 above_daily_limit=True, 56 card_expired=True, 57 ), 58 DENIED 59 ) 60 61 def test_below_limit_right_pin_not_enough_cash(self):the test is still green.
withdraw( right_pin=True, enough_cash=False, above_daily_limit=True, card_expired=True ) -> 'DENIED'I add an assertion for when the right PIN is entered, there is NOT enough cash in the account, it is above limit for daily withdrawals, and the card has NOT expired
PIN
cash
daily limit
card expired
withdrawal
right PIN
NOT enough cash
above limit
NOT expired
DENIED
50 def test_above_limit_right_pin_not_enough_cash(self): 51 self.assertEqual( 52 src.atm.withdraw( 53 right_pin=True, 54 enough_cash=False, 55 above_daily_limit=True, 56 card_expired=True, 57 ), 58 DENIED 59 ) 60 self.assertEqual( 61 src.atm.withdraw( 62 right_pin=True, 63 enough_cash=False, 64 above_daily_limit=True, 65 card_expired=False, 66 ), 67 'CASH' 68 ) 69 70 def test_below_limit_right_pin_not_enough_cash(self):the terminal is my friend, and shows AssertionError
AssertionError: 'DENIED' != 'CASH'
GREEN: make it pass
I change my expectation to match reality in test_above_limit_right_pin_not_enough_cash
60 self.assertEqual(
61 src.atm.withdraw(
62 right_pin=True,
63 enough_cash=False,
64 above_daily_limit=True,
65 card_expired=False,
66 ),
67 DENIED
68 )
69
70 def test_below_limit_right_pin_not_enough_cash(self):
the test passes.
withdraw(
right_pin=True, enough_cash=False,
above_daily_limit=True, card_expired=False
) -> 'DENIED'
withdraw(
right_pin=True, enough_cash=False,
above_daily_limit=True, card_expired=True
) -> 'DENIED'
REFACTOR: make it better
I change the name of the test from test_above_limit_right_pin_not_enough_cash to test_card_w_above_limit_right_pin_not_enough_cash
40 self.assertEqual( 41 src.atm.withdraw( 42 right_pin=True, 43 enough_cash=True, 44 above_daily_limit=True, 45 card_expired=False, 46 ), 47 DENIED 48 ) 49 50 def test_card_w_above_limit_right_pin_not_enough_cash(self): 51 self.assertEqual( 52 src.atm.withdraw( 53 right_pin=True, 54 enough_cash=False, 55 above_daily_limit=True, 56 card_expired=True, 57 ), 58 DENIED 59 )I add a git commit message in the other terminal
git commit -am \ 'add test_card_w_above_limit_right_pin_not_enough_cash'
test_card_w_below_limit_right_pin_not_enough_cash
The truth table for if the right PIN is entered AND there is NOT enough cash in the account, AND it is NOT above limit for daily withdrawals is
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
NOT enough cash |
NOT above limit |
expired |
DENIED |
right PIN |
NOT enough cash |
NOT above limit |
NOT expired |
DENIED |
RED: make it fail
I go back to the terminal where the tests are running
I add values for the
card_expiredandabove_daily_limitparameters to the call to thewithdrawfunction for the case where the right PIN is entered, there is NOT enough cash in the account, it is NOT above limit for daily withdrawals, and the card has expired in test_below_limit_right_pin_not_enough_cashPIN
cash
daily limit
card expired
withdrawal
right PIN
NOT enough cash
NOT above limit
expired
DENIED
70 def test_below_limit_right_pin_not_enough_cash(self): 71 self.assertEqual( 72 src.atm.withdraw( 73 right_pin=True, 74 enough_cash=False, 75 above_daily_limit=False, 76 card_expired=True, 77 ), 78 DENIED 79 ) 80 81 def test_above_limit_wrong_pin_enough_cash(self):the test is still green.
withdraw( right_pin=True, enough_cash=False, above_daily_limit=False, card_expired=True ) -> 'DENIED'I add an assertion for when the right PIN is entered, there is NOT enough cash in the account, it is NOT above limit for daily withdrawals, and the card has NOT expired
PIN
cash
daily limit
card expired
withdrawal
right PIN
NOT enough cash
NOT above limit
NOT expired
DENIED
70 def test_below_limit_right_pin_not_enough_cash(self): 71 self.assertEqual( 72 src.atm.withdraw( 73 right_pin=True, 74 enough_cash=False, 75 above_daily_limit=False, 76 card_expired=True, 77 ), 78 DENIED 79 ) 80 self.assertEqual( 81 src.atm.withdraw( 82 right_pin=True, 83 enough_cash=False, 84 above_daily_limit=False, 85 card_expired=False, 86 ), 87 'CASH' 88 ) 89 90 def test_above_limit_wrong_pin_enough_cash(self):the terminal is my friend, and shows AssertionError
AssertionError: 'DENIED' != 'CASH'
GREEN: make it pass
I change ‘CASH’ to DENIED in test_below_limit_right_pin_not_enough_cash
80 self.assertEqual(
81 src.atm.withdraw(
82 right_pin=True,
83 enough_cash=False,
84 above_daily_limit=False,
85 card_expired=False,
86 ),
87 DENIED
88 )
89
90 def test_above_limit_wrong_pin_enough_cash(self):
the test passes.
withdraw(
right_pin=True, enough_cash=False,
above_daily_limit=False, card_expired=False
) -> 'DENIED'
withdraw(
right_pin=True, enough_cash=False,
above_daily_limit=False, card_expired=True
) -> 'DENIED'
REFACTOR: make it better
I change the name of the test from test_below_limit_right_pin_not_enough_cash to test_card_w_below_limit_right_pin_not_enough_cash
60 self.assertEqual( 61 src.atm.withdraw( 62 right_pin=True, 63 enough_cash=False, 64 above_daily_limit=True, 65 card_expired=False, 66 ), 67 DENIED 68 ) 69 70 def test_card_w_below_limit_right_pin_not_enough_cash(self): 71 self.assertEqual( 72 src.atm.withdraw( 73 right_pin=True, 74 enough_cash=False, 75 above_daily_limit=False, 76 card_expired=True, 77 ), 78 DENIED 79 )I add a git commit message in the other terminal
git commit -am \ 'add test_card_w_below_limit_right_pin_not_enough_cash'
test_card_w_above_limit_wrong_pin_enough_cash
The truth table for if the wrong PIN is entered, AND there is enough cash in the account, AND it is above limit for daily withdrawals is
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
enough cash |
above limit |
expired |
DENIED |
wrong PIN |
enough cash |
above limit |
NOT expired |
DENIED |
RED: make it fail
I go back to the terminal where the tests are running
I add a value for the
card_expiredparameter to the assertion in test_above_limit_wrong_pin_enough_cash for the case where the wrong PIN is entered, there is enough cash in the account, it is above limit for daily withdrawals, and the card has expiredPIN
cash
daily limit
card expired
withdrawal
wrong PIN
enough cash
above limit
expired
DENIED
90 def test_above_limit_wrong_pin_enough_cash(self): 91 self.assertEqual( 92 src.atm.withdraw( 93 right_pin=False, 94 enough_cash=True, 95 above_daily_limit=True, 96 card_expired=True, 97 ), 98 DENIED 99 ) 100 101 def test_wrong_pin_enough_cash_below_limit(self):the test is still green.
withdraw( right_pin=False, enough_cash=True, above_daily_limit=True, card_expired=True ) -> 'DENIED'I add an assertion for the case where the wrong PIN is entered, there is enough cash in the account, it is above limit for daily withdrawals, and the card has NOT expired
PIN
cash
daily limit
card expired
withdrawal
wrong PIN
enough cash
above limit
NOT expired
DENIED
90 def test_above_limit_wrong_pin_enough_cash(self): 91 self.assertEqual( 92 src.atm.withdraw( 93 right_pin=False, 94 enough_cash=True, 95 above_daily_limit=True, 96 card_expired=True, 97 ), 98 DENIED 99 ) 100 self.assertEqual( 101 src.atm.withdraw( 102 right_pin=False, 103 enough_cash=True, 104 above_daily_limit=True, 105 card_expired=False, 106 ), 107 'CASH' 108 ) 109 110 def test_wrong_pin_enough_cash_below_limit(self):the terminal is my friend, and shows AssertionError
AssertionError: 'DENIED' != 'CASH'
GREEN: make it pass
I change my expectation to match reality in the second assertion in test_above_limit_wrong_pin_enough_cash
100 self.assertEqual(
101 src.atm.withdraw(
102 right_pin=False,
103 enough_cash=True,
104 above_daily_limit=True,
105 card_expired=False,
106 ),
107 DENIED
108 )
109
110 def test_wrong_pin_enough_cash_below_limit(self):
the test passes.
withdraw(
right_pin=False, enough_cash=True,
above_daily_limit=True, card_expired=False
) -> 'DENIED'
withdraw(
right_pin=False, enough_cash=True,
above_daily_limit=True, card_expired=True
) -> 'DENIED'
REFACTOR: make it better
I change the name from test_above_limit_wrong_pin_enough_cash to test_card_w_above_limit_wrong_pin_enough_cash
80 self.assertEqual( 81 src.atm.withdraw( 82 right_pin=True, 83 enough_cash=False, 84 above_daily_limit=False, 85 card_expired=False, 86 ), 87 DENIED 88 ) 89 90 def test_card_w_above_limit_wrong_pin_enough_cash(self): 91 self.assertEqual( 92 src.atm.withdraw( 93 right_pin=False, 94 enough_cash=True, 95 above_daily_limit=True, 96 card_expired=True, 97 ), 98 DENIED 99 )I add a git commit message in the other terminal
git commit -am \ 'add test_card_w_above_limit_wrong_pin_enough_cash'
test_card_w_below_limit_wrong_pin_enough_cash
The truth table for if the wrong PIN is entered, AND there is enough cash in the account, AND it is NOT above limit for daily withdrawals is
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
enough cash |
NOT above limit |
expired |
DENIED |
wrong PIN |
enough cash |
NOT above limit |
NOT expired |
DENIED |
RED: make it fail
I add values for the
card_expiredandabove_daily_limitparameters to the assertion in test_below_limit_wrong_pin_enough_cash for the case where the wrong PIN is entered, there is enough cash in the account, it is NOT above limit for daily withdrawals, and the card has expiredPIN
cash
daily limit
card expired
withdrawal
wrong PIN
enough cash
NOT above limit
expired
DENIED
110 def test_below_limit_wrong_pin_enough_cash(self): 111 self.assertEqual( 112 src.atm.withdraw( 113 right_pin=False, 114 enough_cash=True, 115 above_daily_limit=False, 116 card_expired=True, 117 ), 118 DENIED 119 ) 120 121 def test_above_limit_wrong_pin_not_enough_cash(self):the test is still green.
withdraw( right_pin=False, enough_cash=True, above_daily_limit=False, card_expired=True ) -> 'DENIED'I add an assertion for when the wrong PIN is entered, there is enough cash in the account, it is NOT above limit for daily withdrawals, and the card has NOT expired
PIN
cash
daily limit
card expired
withdrawal
wrong PIN
enough cash
NOT above limit
NOT expired
DENIED
110 def test_below_limit_wrong_pin_enough_cash(self): 111 self.assertEqual( 112 src.atm.withdraw( 113 right_pin=False, 114 enough_cash=True, 115 above_daily_limit=False, 116 card_expired=True, 117 ), 118 DENIED 119 ) 120 self.assertEqual( 121 src.atm.withdraw( 122 right_pin=False, 123 enough_cash=True, 124 above_daily_limit=False, 125 card_expired=False, 126 ), 127 'CASH' 128 ) 129 130 def test_above_limit_wrong_pin_not_enough_cash(self):the terminal is my friend, and shows AssertionError
AssertionError: 'DENIED' != 'CASH'
GREEN: make it pass
I change ‘CASH’ to DENIED in test_below_limit_wrong_pin_enough_cash
120 self.assertEqual(
121 src.atm.withdraw(
122 right_pin=False,
123 enough_cash=True,
124 above_daily_limit=False,
125 card_expired=False,
126 ),
127 DENIED
128 )
129
130 def test_above_limit_wrong_pin_not_enough_cash(self):
the test passes.
withdraw(
right_pin=False, enough_cash=True,
above_daily_limit=False, card_expired=False
) -> 'DENIED'
withdraw(
right_pin=False, enough_cash=True,
above_daily_limit=False, card_expired=True
) -> 'DENIED'
REFACTOR: make it better
I change the name of test_below_limit_wrong_pin_enough_cash to test_card_w_below_limit_wrong_pin_enough_cash
100 self.assertEqual( 101 src.atm.withdraw( 102 right_pin=True, 103 enough_cash=False, 104 above_daily_limit=False, 105 card_expired=True, 106 ), 107 DENIED 108 ) 109 110 def test_wrong_pin_enough_cash_w_card(self): 111 self.assertEqual( 112 src.atm.withdraw( 113 right_pin=False, 114 enough_cash=True, 115 above_daily_limit=True, 116 card_expired=True, 117 ), 118 DENIED 119 )I add a git commit message in the other terminal
git commit -am \ 'add test_card_w_below_limit_wrong_pin_enough_cash'
test_card_w_above_limit_wrong_pin_not_enough_cash
The truth table for if the wrong PIN is entered, AND there is NOT enough cash in the account, AND it is above limit for daily withdrawals is
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
NOT enough cash |
above limit |
expired |
DENIED |
wrong PIN |
NOT enough cash |
above limit |
NOT expired |
DENIED |
RED: make it fail
I go back to the terminal where the tests are running
I add a value for the
card_expiredparameter to the assertion in test_above_limit_wrong_pin_not_enough_cash for when the wrong PIN is entered, there is NOT enough cash in the account, it is above limit for daily withdrawals, and the card has expiredPIN
cash
daily limit
card expired
withdrawal
wrong PIN
NOT enough cash
above limit
expired
DENIED
130 def test_above_limit_wrong_pin_not_enough_cash(self): 131 self.assertEqual( 132 src.atm.withdraw( 133 right_pin=False, 134 enough_cash=False, 135 above_daily_limit=True, 136 card_expired=True, 137 ), 138 DENIED 139 ) 140 141 def test_below_limit_wrong_pin_not_enough_cash(self):the test is still green.
withdraw( right_pin=False, enough_cash=False, above_daily_limit=True, card_expired=True ) -> 'DENIED'I add an assertion, for when the wrong PIN is entered, there is NOT enough cash in the account, it is above limit for daily withdrawals, and the card has NOT expired
PIN
cash
daily limit
card expired
withdrawal
wrong PIN
NOT enough cash
above limit
NOT expired
DENIED
130 def test_above_limit_wrong_pin_not_enough_cash(self): 131 self.assertEqual( 132 src.atm.withdraw( 133 right_pin=False, 134 enough_cash=False, 135 above_daily_limit=True, 136 card_expired=True, 137 ), 138 DENIED 139 ) 140 self.assertEqual( 141 src.atm.withdraw( 142 right_pin=False, 143 enough_cash=False, 144 above_daily_limit=True, 145 card_expired=False, 146 ), 147 'CASH' 148 ) 149 150 def test_below_limit_wrong_pin_not_enough_cash(self):the terminal is my friend, and shows AssertionError
AssertionError: 'DENIED' != 'CASH'
GREEN: make it pass
I change ‘CASH’ to DENIED in the second assertion of test_above_limit_wrong_pin_not_enough_cash
140 self.assertEqual(
141 src.atm.withdraw(
142 right_pin=False,
143 enough_cash=False,
144 above_daily_limit=True,
145 card_expired=False,
146 ),
147 DENIED
148 )
149
150 def test_below_limit_wrong_pin_not_enough_cash(self):
the test passes.
withdraw(
right_pin=False, enough_cash=False,
above_daily_limit=True, card_expired=False
) -> 'DENIED'
withdraw(
right_pin=False, enough_cash=False,
above_daily_limit=True, card_expired=True
) -> 'DENIED'
REFACTOR: make it better
I change the name of test_above_limit_wrong_pin_not_enough_cash to test_card_w_above_limit_wrong_pin_not_enough_cash
120 self.assertEqual( 121 src.atm.withdraw( 122 right_pin=False, 123 enough_cash=True, 124 above_daily_limit=False, 125 card_expired=False, 126 ), 127 DENIED 128 ) 129 130 def test_card_w_above_limit_wrong_pin_not_enough_cash(self): 131 self.assertEqual( 132 src.atm.withdraw( 133 right_pin=False, 134 enough_cash=False, 135 above_daily_limit=True, 136 card_expired=True, 137 ), 138 DENIED 139 )I add a git commit message
git commit -am \ 'add test_card_w_above_limit_wrong_pin_not_enough_cash'
test_card_w_below_limit_wrong_pin_not_enough_cash
The truth table for if the wrong PIN is entered, AND there is NOT enough cash in the account, AND it is NOT above limit for daily withdrawals is
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
NOT enough cash |
NOT above limit |
expired |
DENIED |
wrong PIN |
NOT enough cash |
NOT above limit |
NOT expired |
DENIED |
RED: make it fail
I go back to the terminal where the tests are running
I add values for the
card_expiredandabove_daily_limitparameters to the assertion in test_below_limit_wrong_pin_not_enough_cash for when the wrong PIN is entered, there is NOT enough cash in the account, it is NOT above limit for daily withdrawals and the card has expiredPIN
cash
daily limit
card expired
withdrawal
wrong PIN
NOT enough cash
NOT above limit
expired
DENIED
150 def test_below_limit_wrong_pin_not_enough_cash(self): 151 self.assertEqual( 152 src.atm.withdraw( 153 right_pin=False, 154 enough_cash=False, 155 above_daily_limit=False, 156 card_expired=True, 157 ), 158 DENIED 159 ) 160 161 162# Exceptions seenthe test is still green.
withdraw( right_pin=False, enough_cash=False, above_daily_limit=False, card_expired=True ) -> 'DENIED'I add an assertion for when the wrong PIN is entered, there is NOT enough cash in the account, it is NOT above limit for daily withdrawals, and the card has NOT expired
PIN
cash
daily limit
card expired
withdrawal
wrong PIN
NOT enough cash
NOT above limit
NOT expired
DENIED
150 def test_below_limit_wrong_pin_not_enough_cash(self): 151 self.assertEqual( 152 src.atm.withdraw( 153 right_pin=False, 154 enough_cash=False, 155 above_daily_limit=False, 156 card_expired=True, 157 ), 158 DENIED 159 ) 160 self.assertEqual( 161 src.atm.withdraw( 162 right_pin=False, 163 enough_cash=False, 164 above_daily_limit=False, 165 card_expired=False, 166 ), 167 'CASH' 168 ) 169 170 171# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: 'DENIED' != 'CASH'
GREEN: make it pass
I change my expectation to match reality in test_below_limit_wrong_pin_not_enough_cash
160 self.assertEqual(
161 src.atm.withdraw(
162 right_pin=False,
163 enough_cash=False,
164 above_daily_limit=False,
165 card_expired=False,
166 ),
167 DENIED
168 )
169
170
171# Exceptions seen
172# AssertionError
173# NameError
174# AttributeError
175# TypeError
176# SyntaxError
the test passes.
withdraw(
right_pin=False, enough_cash=False,
above_daily_limit=False, card_expired=False
) -> 'DENIED'
withdraw(
right_pin=False, enough_cash=False,
above_daily_limit=False, card_expired=True
) -> 'DENIED'
REFACTOR: make it better
I change the name from test_below_limit_wrong_pin_not_enough_cash to test_card_w_below_limit_wrong_pin_not_enough_cash
140 self.assertEqual( 141 src.atm.withdraw( 142 right_pin=False, 143 enough_cash=False, 144 above_daily_limit=True, 145 card_expired=False, 146 ), 147 DENIED 148 ) 149 150 def test_card_w_below_limit_wrong_pin_not_enough_cash(self): 151 self.assertEqual( 152 src.atm.withdraw( 153 right_pin=False, 154 enough_cash=False, 155 above_daily_limit=False, 156 card_expired=True, 157 ), 158 DENIED 159 )I add a git commit message in the other terminal
git commit -am \ 'add test_card_w_below_limit_wrong_pin_not_enough_cash'
When the withdraw function is called
it returns ‘DENIED’ if the card has expired.
withdraw( right_pin=True, enough_cash=True, above_daily_limit=False, card_expired=True ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' └── if card_expired: └── return denied if above_daily_limit: return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=True, enough_cash=True, above_daily_limit=True, card_expired=True ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' └── if card_expired: └── return denied if above_daily_limit: return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=True, enough_cash=False, above_daily_limit=True, card_expired=True ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' └── if card_expired: └── return denied if above_daily_limit: return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=True, enough_cash=False, above_daily_limit=False, card_expired=True ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' └── if card_expired: └── return denied if above_daily_limit: return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=False, enough_cash=True, above_daily_limit=True, card_expired=True ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' └── if card_expired: └── return denied if above_daily_limit: return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=False, enough_cash=True, above_daily_limit=False, card_expired=True ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' └── if card_expired: └── return denied if above_daily_limit: return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=False, enough_cash=False, above_daily_limit=True, card_expired=True ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' └── if card_expired: └── return denied if above_daily_limit: return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=False, enough_cash=False, above_daily_limit=False, card_expired=True ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' └── if card_expired: └── return denied if above_daily_limit: return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'it returns ‘DENIED’ if the account is above limit for daily withdrawals.
withdraw( right_pin=True, enough_cash=True, above_daily_limit=True, card_expired=False ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' ├── if card_expired: │ return denied └── if above_daily_limit: └── return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=True, enough_cash=False, above_daily_limit=True, card_expired=False ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' ├── if card_expired: │ return denied └── if above_daily_limit: └── return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=False, enough_cash=True, above_daily_limit=True, card_expired=False ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' ├── if card_expired: │ return denied └── if above_daily_limit: └── return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=False, enough_cash=False, above_daily_limit=True, card_expired=False ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' ├── if card_expired: │ return denied └── if above_daily_limit: └── return denied if not right_pin: return denied if not enough_cash: return denied return 'CASH'it only checks if the account is above limit for daily withdrawals if the card has NOT expired
it returns ‘DENIED’ if the wrong PIN is entered.
withdraw( right_pin=False, enough_cash=True, above_daily_limit=False, card_expired=False ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' ├── if card_expired: │ return denied ├── if above_daily_limit: │ return denied └── if not right_pin: └── return denied if not enough_cash: return denied return 'CASH'withdraw( right_pin=False, enough_cash=False, above_daily_limit=False, card_expired=False ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' ├── if card_expired: │ return denied ├── if above_daily_limit: │ return denied └── if not right_pin: └── return denied if not enough_cash: return denied return 'CASH'it only checks if the right PIN is entered if the account is NOT above limit for daily withdrawals.
it returns ‘DENIED’ if there is NOT enough cash in the account.
withdraw( right_pin=True, enough_cash=False, above_daily_limit=False, card_expired=False ) -> 'DENIED' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' ├── if card_expired: │ return denied ├── if above_daily_limit: │ return denied ├── if not right_pin: │ return denied └── if not enough_cash: └── return denied return 'CASH'it only checks if there is enough cash in the account if the right PIN is entered.
it gives me ‘CASH’ if the above conditions are NOT met.
withdraw( right_pin=True, enough_cash=True, above_daily_limit=False, card_expired=False ) -> 'CASH' └── def withdraw( right_pin, enough_cash, above_daily_limit=False, card_expired=False, ): ├── denied = 'DENIED' ├── if card_expired: │ return denied ├── if above_daily_limit: │ return denied ├── if not right_pin: │ return denied ├── if not enough_cash: │ return denied └── return 'CASH'
The withdraw function can also be written with Logical Disjunction (OR) or Logical Conjunction (AND). Try it and see which one you like.
close the project
review
I ran tests for an Automated Teller Machine with these inputs:
has the card expired?
is the PIN correct?
is there enough cash in the account for the withdrawal?
will the withdrawal put the account above or below the daily limit for withdrawals?
the inputs gave me this truth table
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
enough cash |
above limit |
expired |
DENIED |
right PIN |
enough cash |
above limit |
NOT expired |
DENIED |
right PIN |
enough cash |
NOT above limit |
expired |
DENIED |
right PIN |
enough cash |
NOT above limit |
NOT expired |
CASH |
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
NOT enough cash |
above limit |
expired |
DENIED |
right PIN |
NOT enough cash |
above limit |
NOT expired |
DENIED |
right PIN |
NOT enough cash |
NOT above limit |
expired |
DENIED |
right PIN |
NOT enough cash |
NOT above limit |
NOT expired |
DENIED |
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
enough cash |
above limit |
expired |
DENIED |
wrong PIN |
enough cash |
above limit |
NOT expired |
DENIED |
wrong PIN |
enough cash |
NOT above limit |
expired |
DENIED |
wrong PIN |
enough cash |
NOT above limit |
NOT expired |
DENIED |
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
NOT enough cash |
above limit |
expired |
DENIED |
wrong PIN |
NOT enough cash |
above limit |
NOT expired |
DENIED |
wrong PIN |
NOT enough cash |
NOT above limit |
expired |
DENIED |
wrong PIN |
NOT enough cash |
NOT above limit |
NOT expired |
DENIED |
The ATM only gives me 'CASH' when the right PIN is entered, there is enough cash in the account, it is NOT above limit for daily withdrawals, and the bank card has NOT expired.
What if I want the ATM to give a different message with each denial, so that the user knows why the withdrawal failed? The truth table could then be
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
enough cash |
above limit |
expired |
DENIED: Card Expired |
right PIN |
enough cash |
above limit |
NOT expired |
DENIED: You have exceeded the daily withdrawal limit |
right PIN |
enough cash |
NOT above limit |
expired |
DENIED: Card Expired |
right PIN |
enough cash |
NOT above limit |
NOT expired |
CASH |
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
NOT enough cash |
above limit |
expired |
DENIED: Card Expired |
right PIN |
NOT enough cash |
above limit |
NOT expired |
DENIED: You have exceeded the daily withdrawal limit |
right PIN |
NOT enough cash |
NOT above limit |
expired |
DENIED: Card Expired |
right PIN |
NOT enough cash |
NOT above limit |
NOT expired |
DENIED: There is not enough Cash in the Account |
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
enough cash |
above limit |
expired |
DENIED: Card Expired |
wrong PIN |
enough cash |
above limit |
NOT expired |
DENIED: You entered the wrong PIN. Try again… |
wrong PIN |
enough cash |
NOT above limit |
expired |
DENIED: Card Expired |
wrong PIN |
enough cash |
NOT above limit |
NOT expired |
DENIED: You entered the wrong PIN. Try again… |
PIN |
cash |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
NOT enough cash |
above limit |
expired |
DENIED: Card Expired |
wrong PIN |
NOT enough cash |
above limit |
NOT expired |
DENIED: You entered the wrong PIN. Try again… |
wrong PIN |
NOT enough cash |
NOT above limit |
expired |
DENIED: Card Expired |
wrong PIN |
NOT enough cash |
NOT above limit |
NOT expired |
DENIED: You entered the wrong PIN. Try again… |
What would I change in the tests and solution?
code from the chapter
what is next?
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.