Automated Teller Machine
I want to make an Automated Teller Machine that allows withdrawals or denies them, if the inputs are
is the PIN correct?
is the amount I want to take, smaller or bigger than what is in the account?
this is the truth table I get
PIN |
money |
withdrawal |
|---|---|---|
right PIN |
enough money |
CASH |
right PIN |
NOT enough money |
DENIED |
wrong PIN |
enough money |
DENIED |
wrong PIN |
NOT enough money |
DENIED |
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_right_pin_enough_money_w_card(self):
11 self.assertEqual(
12 src.atm.withdraw(
13 right_pin=True,
14 enough_money=True,
15 above_daily_limit=True,
16 card_expired=True,
17 ),
18 DENIED
19 )
20
21 self.assertEqual(
22 src.atm.withdraw(
23 right_pin=True,
24 enough_money=True,
25 above_daily_limit=True,
26 card_expired=False,
27 ),
28 DENIED
29 )
30
31 self.assertEqual(
32 src.atm.withdraw(
33 right_pin=True,
34 enough_money=True,
35 above_daily_limit=False,
36 card_expired=True,
37 ),
38 DENIED
39 )
40
41 self.assertEqual(
42 src.atm.withdraw(
43 right_pin=True,
44 enough_money=True,
45 above_daily_limit=False,
46 card_expired=False,
47 ),
48 'CASH'
49 )
50
51 def test_right_pin_not_enough_money_w_card(self):
52 self.assertEqual(
53 src.atm.withdraw(
54 right_pin=True,
55 enough_money=False,
56 above_daily_limit=True,
57 card_expired=True,
58 ),
59 DENIED
60 )
61
62 self.assertEqual(
63 src.atm.withdraw(
64 right_pin=True,
65 enough_money=False,
66 above_daily_limit=True,
67 card_expired=False,
68 ),
69 DENIED
70 )
71
72 self.assertEqual(
73 src.atm.withdraw(
74 right_pin=True,
75 enough_money=False,
76 above_daily_limit=False,
77 card_expired=True,
78 ),
79 DENIED
80 )
81
82 self.assertEqual(
83 src.atm.withdraw(
84 right_pin=True,
85 enough_money=False,
86 above_daily_limit=False,
87 card_expired=True,
88 ),
89 DENIED
90 )
91
92 def test_wrong_pin_enough_money_w_card(self):
93 self.assertEqual(
94 src.atm.withdraw(
95 right_pin=False,
96 enough_money=True,
97 above_daily_limit=True,
98 card_expired=True,
99 ),
100 DENIED
101 )
102
103 self.assertEqual(
104 src.atm.withdraw(
105 right_pin=False,
106 enough_money=True,
107 above_daily_limit=True,
108 card_expired=False,
109 ),
110 DENIED
111 )
112
113 self.assertEqual(
114 src.atm.withdraw(
115 right_pin=False,
116 enough_money=True,
117 above_daily_limit=False,
118 card_expired=True,
119 ),
120 DENIED
121 )
122
123 self.assertEqual(
124 src.atm.withdraw(
125 right_pin=False,
126 enough_money=True,
127 above_daily_limit=False,
128 card_expired=False,
129 ),
130 DENIED
131 )
132
133 def test_wrong_pin_not_enough_money_w_card(self):
134 self.assertEqual(
135 src.atm.withdraw(
136 right_pin=False,
137 enough_money=False,
138 above_daily_limit=True,
139 card_expired=True,
140 ),
141 DENIED
142 )
143
144 self.assertEqual(
145 src.atm.withdraw(
146 right_pin=False,
147 enough_money=False,
148 above_daily_limit=True,
149 card_expired=False,
150 ),
151 DENIED
152 )
153
154 self.assertEqual(
155 src.atm.withdraw(
156 right_pin=False,
157 enough_money=False,
158 above_daily_limit=False,
159 card_expired=True,
160 ),
161 DENIED
162 )
163
164 self.assertEqual(
165 src.atm.withdraw(
166 right_pin=False,
167 enough_money=False,
168 above_daily_limit=False,
169 card_expired=False,
170 ),
171 DENIED
172 )
173
174
175# Exceptions seen
176# AssertionError
177# NameError
178# AttributeError
179# TypeError
180# SyntaxError
requirements
start the project
I name this project
atmI open a terminal
I use uv to make a directory for the project and initialize it
uv init atmthe terminal shows
Initialized project `atm` at `.../pumping_python/atm`then goes back to the command line.
I change directory to the project
cd atmthe terminal shows I am in the
atmfolder.../pumping_python/atmI make a directory for the source code
mkdir srcthe terminal goes back to the command line.
I use the mv program to change the name of
main.pytoatm.pyand move it to thesrcfoldermv main.py src/atm.pyMove-Item main.py src/atm.pythe terminal goes back to the command line.
I make a directory for the tests
mkdir teststhe terminal goes back to the command line.
I make the
testsdirectory a Python packageDanger
use 2 underscores (__) before and after
initfor__init__.pynot_init_.pytouch tests/__init__.pyNew-Item tests/__init__.pythe terminal goes back to the command line.
I make a Python file for the tests in the
testsdirectorytouch tests/test_atm.pyNew-Item tests/test_atm.pythe terminal goes back to the command line.
I open
test_atm.pyin the editor of the Integrated Development Environment (IDE)Tip
I can open a file from the terminal in the Integrated Development Environment (IDE) with the name of the program and the name of the file. That means if I type this in the terminal
code tests/test_atm.pyVisual Studio Code opens
test_atm.pyin the editorI add the first failing test to
test_atm.py1import unittest 2 3 4class TestATM(unittest.TestCase): 5 6 def test_failure(self): 7 self.assertFalse(True)I go back to the terminal to make a requirements file for the Python packages I need
echo "pytest" > requirements.txtthe terminal goes back to the command line.
I add pytest-watcher to the requirements file
echo "pytest-watcher" >> requirements.txtthe terminal goes back to the command line.
I install the Python packages that I wrote in the requirements file
uv add --requirement requirements.txtthe terminal shows that it installed the Python packages
I use tree to look at the structure of the project
treethe terminal shows
. ├── README.md ├── pyproject.toml ├── requirements.txt ├── src │ └── atm.py ├── tests │ ├── __init__.py │ └── test_atm.py └── uv.lockif you do not see
uv.lockin your tree, make sure you ranuv add --requirement requirements.txt, then run the tests nextI use pytest-watcher to run the tests automatically
uv run pytest-watcher . --nowthe 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 ====================if the terminal does not show the same error, then check
if your
tests/__init__.pyhas two underscores (__) before and afterinitfor__init__.pynot_init_.pyif you ran
echo "pytest-watcher" >> requirements.txt, to addpytest-watcherto the requirements file
fix those errors and try to run
uv run pytest-watcher . --nowagainI add AssertionError to the list of Exceptions seen in
test_atm.py4class TestATM(unittest.TestCase): 5 6 def test_failure(self): 7 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.
test_right_pin_enough_money
RED: make it fail
I change test_failure to test_right_pin_enough_money, then add an assertion for when the right PIN is entered and there is enough money in the account
PIN |
money |
withdrawal |
|---|---|---|
right PIN |
enough money |
CASH |
4class TestATM(unittest.TestCase):
5
6 def test_right_pin_enough_money(self):
7 my_expectation = 'CASH'
8 reality = src.atm.withdraw(
9 right_pin=True,
10 enough_money=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.pyin thesrcfolder does not have anything namedwithdrawin itIf you get ModuleNotFoundError
ModuleNotFoundError: No module named 'src'check if you have
__init__.pyin thetestsfolder with underscores (__) before and afterinitfor__init__.pynot_init_.py, then add ModuleNotFoundError to the list of Exceptions seenI add AttributeError to the list of Exceptions seen
16# Exceptions seen 17# AssertionError 18# NameError 19# AttributeErrorI use the Explorer to open
atm.pyfrom thesrcfolder in the editorI add a function to
atm.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 two keyword arguments and this definition only takes calls with 0 argumentsI add TypeError to the list of Exceptions seen in
test_atm.py16# Exceptions seen 17# AssertionError 18# NameError 19# AttributeError 20# TypeErrorI add the keyword argument to the function in
atm.py1def withdraw(right_pin): 2 return Nonethe terminal is my friend, and shows TypeError
TypeError: withdraw() got an unexpected keyword argument 'enough_money'because the test called the
withdrawfunction with two keyword arguments and this definition only takes calls with 1 inputI add
enough_moneyto the function signature1def withdraw(right_pin, enough_money): 2 return Nonethe terminal is my friend, and shows AssertionError
AssertionError: None != 'CASH'the
withdrawfunction always returns None and the assertion expects'CASH'I change the return statement to give me
'CASH'1def withdraw(right_pin, enough_money): 2 return 'CASH'the test passes, this ATM works. The
withdrawfunction always returns ‘CASH’, it does not care about the inputs. Is this Tautology or ‘CASH’ that never ends?
test_right_pin_not_enough_money
RED: make it fail
I add a test with an assertion for when the right PIN is entered AND there is NOT enough money in the account
PIN
money
withdrawal
right PIN
NOT enough money
DENIED
7 def test_right_pin_enough_money(self): 8 my_expectation = 'CASH' 9 reality = src.atm.withdraw( 10 right_pin=True, 11 enough_money=True, 12 ) 13 self.assertEqual(reality, my_expectation) 14 15 def test_right_pin_not_enough_money(self): 16 my_expectation = 'DENIED' 17 reality = src.atm.withdraw( 18 right_pin=True, 19 enough_money=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’
GREEN: make it pass
I add an if statement to atm.py
1def withdraw(right_pin, enough_money):
2 if enough_money == False:
3 return 'DENIED'
4
5 return 'CASH'
the test passes.
REFACTOR: make it better
I use the bool built-in function
1def withdraw(right_pin, enough_money): 2 # if enough_money == False: 3 if bool(enough_money) == False: 4 return 'DENIED' 5 6 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_money): 2 # if enough_money == False: 3 # if bool(enough_money) == False: 4 if not bool(enough_money) == True: 5 return 'DENIED' 6 7 return 'CASH'still green.
I remove
== True1def withdraw(right_pin, enough_money): 2 # if enough_money == False: 3 # if bool(enough_money) == False: 4 # if not bool(enough_money) == True: 5 if not bool(enough_money): 6 return 'DENIED' 7 8 return 'CASH'green.
I remove bool
1def withdraw(right_pin, enough_money): 2 # if enough_money == False: 3 # if bool(enough_money) == False: 4 # if not bool(enough_money) == True: 5 # if not bool(enough_money): 6 if not enough_money: 7 return 'DENIED' 8 9 return 'CASH'still green, because
if bool(something) == Falseis the same asif not bool(something) == Trueis the same asif not somethingI remove the commented lines
1def withdraw(right_pin, enough_money): 2 if not enough_money: 3 return 'DENIED' 4 5 return 'CASH'the test is still green.
This is what happens when the withdraw function is called
it returns ‘DENIED’ if there is NOT enough money in the account
it gives me ‘CASH’ if the :ref:`condition<if statements>`is NOT met
What binary operation is the withdraw function using?
test_wrong_pin_enough_money
RED: make it fail
I add a test with an assertion for when the wrong PIN is entered and there is enough money in the account, to test_atm.py
PIN |
money |
withdrawal |
|---|---|---|
wrong PIN |
enough money |
DENIED |
7 def test_right_pin_not_enough_money(self):
8 my_expectation = 'DENIED'
9 reality = src.atm.withdraw(
10 right_pin=True,
11 enough_money=False,
12 )
13 self.assertEqual(reality, my_expectation)
14
15 def test_wrong_pin_enough_money(self):
16 my_expectation = 'DENIED'
17 reality = src.atm.withdraw(
18 right_pin=False,
19 enough_money=True,
20 )
21 self.assertEqual(reality, my_expectation)
22
23
24# Exceptions seen
the terminal is my friend, and shows AssertionError
AssertionError: 'CASH' != 'DENIED'
because the withdraw function returned ‘CASH’ and the assertion expects DENIED. Why is this ATM giving ‘CASH’ when the wrong PIN is entered?
GREEN: make it pass
I add an if statement for this case to atm.py
1def withdraw(right_pin, enough_money):
2 if right_pin == False:
3 return 'DENIED'
4
5 if not enough_money:
6 return 'DENIED'
7
8 return 'CASH'
the test passes.
REFACTOR: make it better
I add the bool built-in function
1def withdraw(right_pin, enough_money): 2 # if right_pin == False: 3 if bool(right_pin) == False: 4 return 'DENIED' 5 6 if not enough_money: 7 return 'DENIED' 8 9 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_money): 2 # if right_pin == False: 3 # if bool(right_pin) == False: 4 if not bool(right_pin) == True: 5 return 'DENIED' 6 7 if not enough_money: 8 return 'DENIED' 9 10 return 'CASH'still green.
I remove
== True1def withdraw(right_pin, enough_money): 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 8 if not enough_money: 9 return 'DENIED' 10 11 return 'CASH'green.
I remove bool
1def withdraw(right_pin, enough_money): 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 9 if not enough_money: 10 return 'DENIED' 11 12 return 'CASH'still green, because
if bool(something) == Falseis the same asif not bool(something) == Trueis the same asif not somethingI remove the commented lines
1def withdraw(right_pin, enough_money): 2 if not right_pin: 3 return 'DENIED' 4 5 if not enough_money: 6 return 'DENIED' 7 8 return 'CASH'this is what happens when the
withdrawfunction is calledit returns ‘DENIED’ if the wrong PIN is entered
it returns ‘DENIED’ if there is NOT enough money in the account
it gives me ‘CASH’ if the above conditions are NOT met
What binary operation is the
withdrawfunction using now?
test_wrong_pin_not_enough_money
I add a test with an assertion for the last case, which is when the wrong PIN is entered and there is NOT enough money in the account, to test_atm.py
PIN |
money |
withdrawal |
|---|---|---|
wrong PIN |
NOT enough money |
DENIED |
22 def test_wrong_pin_enough_money(self):
23 my_expectation = 'DENIED'
24 reality = src.atm.withdraw(
25 right_pin=False,
26 enough_money=True,
27 )
28 self.assertEqual(reality, my_expectation)
29
30 reality = src.atm.withdraw(
31 right_pin=False,
32 enough_money=False,
33 )
34 self.assertEqual(reality, my_expectation)
35
36
37# Exceptions seen
the test is still green.
REFACTOR: make it better
I add a global variable to remove repetition of ‘DENIED’ from the tests because 3 of them use it, in
test_atm.py1import src.atm 2import unittest 3 4 5DENIED = 'DENIED' 6 7 8class TestATM(unittest.TestCase):this way I do not need to use a variable in each test, they can all use the global variable
I use the new global variable in test_right_pin_not_enough_money_w_limit
34 def test_right_pin_not_enough_money_w_limit(self): 35 # my_expectation = 'DENIED' 36 reality = src.atm.withdraw( 37 right_pin=False, 38 enough_money=False, 39 ) 40 # self.assertEqual(reality, my_expectation) 41 self.assertEqual(reality, DENIED) 42 43 44# Exceptions seenthe test is still green.
I remove the commented lines
34 def test_right_pin_not_enough_money_w_limit(self): 35 reality = src.atm.withdraw( 36 right_pin=False, 37 enough_money=False, 38 ) 39 self.assertEqual(reality, DENIED) 40 41 42# Exceptions seenI use the new global variable in test_wrong_pin_enough_money
26 def test_wrong_pin_enough_money(self): 27 # my_expectation = 'DENIED' 28 reality = src.atm.withdraw( 29 right_pin=False, 30 enough_money=True, 31 ) 32 # self.assertEqual(reality, my_expectation) 33 self.assertEqual(reality, DENIED) 34 35 def test_right_pin_not_enough_money_w_limit(self):still green.
I remove the commented lines
26 def test_wrong_pin_enough_money(self): 27 reality = src.atm.withdraw( 28 right_pin=False, 29 enough_money=True, 30 ) 31 self.assertEqual(reality, DENIED) 32 33 def test_right_pin_not_enough_money_w_limit(self):I use the global variable in test_right_pin_not_enough_money
18 def test_right_pin_not_enough_money(self): 19 # my_expectation = 'DENIED' 20 reality = src.atm.withdraw( 21 right_pin=True, 22 enough_money=False, 23 ) 24 # self.assertEqual(reality, my_expectation) 25 self.assertEqual(reality, DENIED) 26 27 def test_wrong_pin_enough_money(self):green.
I remove the commented lines
18 def test_right_pin_not_enough_money(self): 19 reality = src.atm.withdraw( 20 right_pin=True, 21 enough_money=False, 22 ) 23 self.assertEqual(reality, DENIED) 24 25 def test_wrong_pin_enough_money(self):
test_right_pin_enough_money_w_limit
So far, the truth table for the Automated Teller Machine is
PIN |
money |
withdrawal |
|---|---|---|
right PIN |
enough money |
CASH |
right PIN |
NOT enough money |
DENIED |
wrong PIN |
enough money |
DENIED |
wrong PIN |
NOT enough money |
DENIED |
I want to add a condition for a daily limit on how much can be taken from the account. The inputs for the ATM will then be
is the PIN correct?
is the amount I want to take, smaller or bigger than what is in the account?
will the withdrawal put the account above or below the daily limit for withdrawals?
The truth table for if the right PIN is entered and there is enough money in the account, is
PIN |
money |
daily limit |
withdrawal |
|---|---|---|---|
right PIN |
enough money |
above limit |
DENIED |
right PIN |
enough money |
NOT above limit |
CASH |
RED: make it fail
I add an assertion for the case where the right PIN is entered, there is enough money in the account, and it is above limit for daily withdrawals, to test_right_pin_enough_money
PIN |
money |
daily limit |
withdrawal |
|---|---|---|---|
right PIN |
enough money |
above limit |
DENIED |
7 def test_right_pin_enough_money(self):
8 reality = src.atm.withdraw(
9 right_pin=True,
10 enough_money=True,
11 above_daily_limit=True,
12 )
13 self.assertEqual(reality, DENIED)
14
15 my_expectation = 'CASH'
16 reality = src.atm.withdraw(
17 right_pin=True,
18 enough_money=True,
19 )
20 self.assertEqual(reality, my_expectation)
21
22 def test_right_pin_not_enough_money(self):
the terminal is my friend, and shows TypeError
TypeError: withdraw() got an unexpected keyword argument 'above_daily_limit'
because the withdraw function only takes 2 arguments (right_pin and enough_money) and the new assertion called it with 3 arguments (right_pin, enough_money and above_daily_limit)
GREEN: make it pass
I add
above_daily_limitto the function inatm.py1def withdraw( 2 right_pin, enough_money, 3 above_daily_limit, 4 ): 5 if not right_pin: 6 return 'DENIED' 7 8 if not enough_money: 9 return 'DENIED' 10 11 return 'CASH'the terminal is my friend, and shows
-
AssertionError: 'CASH' != 'DENIED'because the
withdrawfunction returned ‘CASH’ and the assertion expects DENIED. -
FAILED ...test_right_pin_not_enough_money - TypeError: withdraw() missing 1 required positional argument: 'above_daily_limit' FAILED ...test_wrong_pin_enough_money - TypeError: withdraw() missing 1 required positional argument: 'above_daily_limit' FAILED ...test_right_pin_not_enough_money_w_limit - TypeError: withdraw() missing 1 required positional argument: 'above_daily_limit'because the other assertions do not provide a value for
above_daily_limitwhen they call thewithdrawfunction, I have to make it a choice
-
I add a default value for the
above_daily_limitparameter to make it a choice1def withdraw( 2 right_pin, enough_money, 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_money, 3 above_daily_limit=False, 4 ): 5 if above_daily_limit == True: 6 return 'DENIED' 7 8 if not right_pin: 9 return 'DENIED' 10 11 if not enough_money: 12 return 'DENIED' 13 14 return 'CASH'the test passes.
REFACTOR: make it better
I add the bool built-in function
5 # if above_daily_limit == True: 6 if bool(above_daily_limit) == True: 7 return 'DENIED'the test is still green.
I remove
== True5 # if above_daily_limit == True: 6 # if bool(above_daily_limit) == True: 7 if bool(above_daily_limit): 8 return 'DENIED'still green.
I remove bool
1 # if above_daily_limit == True: 2 # if bool(above_daily_limit) == True: 3 # if bool(above_daily_limit): 4 if above_daily_limit: 5 return 'DENIED'green, because
if bool(something) == Trueis the same asif bool(something)is the same asif somethingI add a variable to use them to remove repetition of
'DENIED'1def withdraw( 2 right_pin, enough_money, 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_money, 3 above_daily_limit=False, 4 ): 5 denied = 'DENIED' 6 # if above_daily_limit == True: 7 # if bool(above_daily_limit) == True: 8 # if bool(above_daily_limit): 9 if above_daily_limit: 10 # return 'DENIED' 11 return denied 12 13 if not right_pin: 14 # return 'DENIED' 15 return denied 16 17 if not enough_money: 18 # return 'DENIED' 19 return denied 20 21 return 'CASH'I remove the commented lines
1def withdraw( 2 right_pin, enough_money, 3 above_daily_limit=False, 4 ): 5 denied = 'DENIED' 6 7 if above_daily_limit: 8 return denied 9 10 if not right_pin: 11 return denied 12 13 if not enough_money: 14 return denied 15 16 return 'CASH'this is what happens when the
withdrawfunction is calledit 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 money in the account
it gives me ‘CASH’ if the above conditions are NOT met
REFACTOR: make it better
I do not need to add anything to the next assertion which is for when the right PIN is entered, and there is enough money 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
money
daily limit
withdrawal
right PIN
enough money
NOT above limit
CASH
this means that
src.atm.withdraw( right_pin=True, enough_money=True, )is the same as
src.atm.withdraw( right_pin=True, enough_money=True, above_daily_limit=False, )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_money to test_right_pin_enough_money_w_limit
5class TestATM(unittest.TestCase): 6 7 def test_right_pin_enough_money_w_limit(self): 8 reality = src.atm.withdraw( 9 right_pin=True, 10 enough_money=True, 11 above_daily_limit=True, 12 ) 13 self.assertEqual(reality, DENIED)
test_right_pin_not_enough_money_w_limit
The truth table for if the right PIN is entered and there is NOT enough money in the account, is
PIN |
money |
daily limit |
withdrawal |
|---|---|---|---|
right PIN |
NOT enough money |
above limit |
DENIED |
right PIN |
NOT enough money |
NOT above limit |
DENIED |
I add a value for the
above_daily_limitparameter to the assertion in test_right_pin_not_enough_money, for the case where the right PIN is entered, there is NOT enough money in the account, and it is above limit for daily withdrawals,PIN
money
daily limit
withdrawal
right PIN
NOT enough money
above limit
DENIED
25 def test_right_pin_not_enough_money(self): 26 reality = src.atm.withdraw( 27 right_pin=True, 28 enough_money=False, 29 above_daily_limit=True, 30 ) 31 self.assertEqual(reality, DENIED) 32 33 def test_wrong_pin_enough_money(self):the test is still green.
I add an assertion for when the right PIN is entered, there is NOT enough money in the account, and it is NOT above limit for daily withdrawals
PIN
money
daily limit
withdrawal
right PIN
NOT enough money
above limit
DENIED
25def test_right_pin_not_enough_money(self): 26 reality = src.atm.withdraw( 27 right_pin=True, 28 enough_money=False, 29 above_daily_limit=True, 30 ) 31 self.assertEqual(reality, DENIED) 32 33 reality = src.atm.withdraw( 34 right_pin=True, 35 enough_money=False, 36 ) 37 self.assertEqual(reality, DENIED) 38 39def test_wrong_pin_enough_money(self):still green. 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_money=False, )is the same as
src.atm.withdraw( right_pin=True, enough_money=False, above_daily_limit=False, )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_not_enough_money to test_right_pin_not_enough_money_w_limit
18 my_expectation = 'CASH' 19 reality = src.atm.withdraw( 20 right_pin=True, 21 enough_money=True, 22 ) 23 self.assertEqual(reality, my_expectation) 24 25 def test_right_pin_not_enough_money_w_limit(self): 26 reality = src.atm.withdraw( 27 right_pin=True, 28 enough_money=False, 29 above_daily_limit=True, 30 ) 31 self.assertEqual(reality, DENIED)
test_wrong_pin_enough_money_w_limit
The truth table for if the wrong PIN is entered and there is enough money in the account, is
PIN |
money |
daily limit |
withdrawal |
|---|---|---|---|
wrong PIN |
enough money |
above limit |
DENIED |
wrong PIN |
enough money |
NOT above limit |
DENIED |
I add a value for the
above_daily_limitparameter to the assertion in test_wrong_pin_enough_money, for the case where the wrong PIN is entered, there is enough money in the account, and it is above limit for daily withdrawalsPIN
money
daily limit
withdrawal
wrong PIN
enough money
above limit
DENIED
39 def test_wrong_pin_enough_money(self): 40 reality = src.atm.withdraw( 41 right_pin=False, 42 enough_money=True, 43 above_daily_limit=True, 44 ) 45 self.assertEqual(reality, DENIED) 46 47 def test_wrong_pin_not_enough_money(self):still green.
I add an assertion for when the wrong PIN is entered, there is enough money in the account, and it is NOT above limit for daily withdrawals
PIN
money
daily limit
withdrawal
wrong PIN
enough money
NOT above limit
DENIED
39 def test_wrong_pin_enough_money(self): 40 reality = src.atm.withdraw( 41 right_pin=False, 42 enough_money=True, 43 above_daily_limit=True, 44 ) 45 self.assertEqual(reality, DENIED) 46 47 reality = src.atm.withdraw( 48 right_pin=False, 49 enough_money=True, 50 ) 51 self.assertEqual(reality, DENIED) 52 53 def test_wrong_pin_not_enough_money(self):green. 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_money=True, )is the same as
src.atm.withdraw( right_pin=False, enough_money=True, above_daily_limit=False, )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_wrong_pin_enough_money to test_wrong_pin_enough_money_w_limit
33 reality = src.atm.withdraw( 34 right_pin=True, 35 enough_money=False, 36 ) 37 self.assertEqual(reality, DENIED) 38 39 def test_wrong_pin_enough_money_w_limit(self): 40 reality = src.atm.withdraw( 41 right_pin=False, 42 enough_money=True, 43 above_daily_limit=True, 44 ) 45 self.assertEqual(reality, DENIED)
test_wrong_pin_not_enough_money_w_limit
The truth table for if the wrong PIN is entered and there is NOT enough money in the account, is
PIN |
money |
daily limit |
withdrawal |
|---|---|---|---|
wrong PIN |
NOT enough money |
above limit |
DENIED |
wrong PIN |
NOT enough money |
NOT above limit |
DENIED |
I add
above_daily_limitto the call tosrc.atm.withdrawin test_wrong_pin_not_enough_money, for when the wrong PIN is entered, there is NOT enough money in the account, and it is above limit for daily withdrawalsPIN
money
daily limit
withdrawal
wrong PIN
NOT enough money
above limit
DENIED
53 def test_wrong_pin_not_enough_money(self): 54 reality = src.atm.withdraw( 55 right_pin=False, 56 enough_money=False, 57 above_daily_limit=True, 58 ) 59 self.assertEqual(reality, DENIED) 60 61 62# Exceptions seenstill green.
I add an assertion for when the wrong PIN is entered, there is NOT enough money in the account, and it is NOT above limit for daily withdrawals
PIN
money
daily limit
withdrawal
wrong PIN
NOT enough money
NOT above limit
DENIED
53 def test_wrong_pin_not_enough_money(self): 54 reality = src.atm.withdraw( 55 right_pin=False, 56 enough_money=False, 57 above_daily_limit=True, 58 ) 59 self.assertEqual(reality, DENIED) 60 61 reality = src.atm.withdraw( 62 right_pin=False, 63 enough_money=False, 64 ) 65 self.assertEqual(reality, DENIED) 66 67 68# Exceptions seenthe test is still green. 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_money=False, )is the same as
reality = src.atm.withdraw( right_pin=False, enough_money=False, above_daily_limit=False, )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_wrong_pin_not_enough_money to test_wrong_pin_not_enough_money_w_limit
47 reality = src.atm.withdraw( 48 right_pin=False, 49 enough_money=True, 50 ) 51 self.assertEqual(reality, DENIED) 52 53 def test_wrong_pin_not_enough_money_w_limit(self): 54 reality = src.atm.withdraw( 55 right_pin=False, 56 enough_money=False, 57 above_daily_limit=True, 58 ) 59 self.assertEqual(reality, DENIED)I call the
withdrawfunction directly in test_wrong_pin_not_enough_money_w_limit. I do not need therealityvariable as a middle man because I only use the variables once for each assertion53 def test_wrong_pin_not_enough_money_w_limit(self): 54 reality = src.atm.withdraw( 55 right_pin=False, 56 enough_money=False, 57 above_daily_limit=True, 58 ) 59 # self.assertEqual(reality, DENIED) 60 self.assertEqual( 61 src.atm.withdraw( 62 right_pin=False, 63 enough_money=False, 64 above_daily_limit=True, 65 ), 66 DENIED 67 ) 68 69 reality = src.atm.withdraw( 70 right_pin=False, 71 enough_money=False, 72 ) 73 # self.assertEqual(reality, DENIED) 74 self.assertEqual( 75 src.atm.withdraw( 76 right_pin=False, 77 enough_money=False, 78 ), 79 DENIED 80 )still green.
I remove the commented lines and unused variables from test_wrong_pin_not_enough_money_w_limit
53 def test_wrong_pin_not_enough_money_w_limit(self): 54 self.assertEqual( 55 src.atm.withdraw( 56 right_pin=False, 57 enough_money=False, 58 above_daily_limit=True, 59 ), 60 DENIED 61 ) 62 63 self.assertEqual( 64 src.atm.withdraw( 65 right_pin=False, 66 enough_money=False, 67 ), 68 DENIED 69 ) 70 71 72# Exceptions seenI make the same change in test_wrong_pin_enough_money_w_limit
39 def test_wrong_pin_enough_money_w_limit(self): 40 reality = src.atm.withdraw( 41 right_pin=False, 42 enough_money=True, 43 above_daily_limit=True, 44 ) 45 # self.assertEqual(reality, DENIED) 46 self.assertEqual( 47 src.atm.withdraw( 48 right_pin=False, 49 enough_money=True, 50 above_daily_limit=True, 51 ), 52 DENIED 53 ) 54 55 reality = src.atm.withdraw( 56 right_pin=False, 57 enough_money=True, 58 ) 59 # self.assertEqual(reality, DENIED) 60 self.assertEqual( 61 src.atm.withdraw( 62 right_pin=False, 63 enough_money=True, 64 ), 65 DENIED 66 ) 67 68 def test_wrong_pin_not_enough_money_w_limit(self):green.
I remove the commented lines and unused variables from test_wrong_pin_enough_money_w_limit
39 def test_wrong_pin_enough_money_w_limit(self): 40 self.assertEqual( 41 src.atm.withdraw( 42 right_pin=False, 43 enough_money=True, 44 above_daily_limit=True, 45 ), 46 DENIED 47 ) 48 49 self.assertEqual( 50 src.atm.withdraw( 51 right_pin=False, 52 enough_money=True, 53 ), 54 DENIED 55 ) 56 57 def test_wrong_pin_not_enough_money_w_limit(self):I change test_right_pin_not_enough_money_w_limit
25 def test_right_pin_not_enough_money_w_limit(self): 26 self.assertEqual( 27 src.atm.withdraw( 28 right_pin=True, 29 enough_money=False, 30 above_daily_limit=True, 31 ), 32 DENIED 33 ) 34 35 self.assertEqual( 36 src.atm.withdraw( 37 right_pin=True, 38 enough_money=False, 39 ), 40 DENIED 41 ) 42 43 def test_wrong_pin_enough_money_w_limit(self):still green.
I also change test_right_pin_enough_money_w_limit
10 def test_right_pin_enough_money_w_limit(self): 11 self.assertEqual( 12 src.atm.withdraw( 13 right_pin=True, 14 enough_money=True, 15 above_daily_limit=True, 16 ), 17 DENIED 18 ) 19 20 self.assertEqual( 21 src.atm.withdraw( 22 right_pin=True, 23 enough_money=True, 24 ), 25 'CASH' 26 ) 27 28 def test_right_pin_not_enough_money_w_limit(self):the test is still green.
test_right_pin_enough_money_w_card
The truth table for the Automated Teller Machine is now
PIN |
money |
daily limit |
withdrawal |
|---|---|---|---|
right PIN |
enough money |
above limit |
DENIED |
right PIN |
enough money |
NOT above limit |
CASH |
right PIN |
NOT enough money |
above limit |
DENIED |
right PIN |
NOT enough money |
NOT above limit |
DENIED |
PIN |
money |
daily limit |
withdrawal |
|---|---|---|---|
wrong PIN |
enough money |
above limit |
DENIED |
wrong PIN |
enough money |
NOT above limit |
DENIED |
wrong PIN |
NOT enough money |
above limit |
DENIED |
wrong PIN |
NOT enough money |
NOT above limit |
DENIED |
What if the bank card has expired? The inputs for the ATM will then be
has the card expired?
is the PIN correct?
is the amount I want to take, smaller or bigger than what is in the account?
will the withdrawal put the account above or below the daily limit for withdrawals?
The truth table for if the right PIN is entered, AND there is enough money in the account, is
PIN |
money |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
enough money |
above limit |
expired |
DENIED |
right PIN |
enough money |
above limit |
NOT expired |
DENIED |
right PIN |
enough money |
NOT above limit |
expired |
DENIED |
right PIN |
enough money |
NOT above limit |
NOT expired |
CASH |
RED: make it fail
I add a value for the card_expired parameter to the call to the withdraw function for the case where the right PIN is entered, there is enough money in the account, it is above limit for daily withdrawals, and the card has expired, in the first assertion in test_right_pin_enough_money_w_limit
PIN |
money |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
enough money |
above limit |
expired |
DENIED |
7 def test_right_pin_enough_money_w_limit(self):
8 self.assertEqual(
9 src.atm.withdraw(
10 right_pin=True,
11 enough_money=True,
12 above_daily_limit=True,
13 card_expired=True,
14 ),
15 DENIED
16 )
the terminal is my friend, and shows TypeError
TypeError: withdraw() got an unexpected keyword argument 'card_expired'
because the withdraw function only takes 2 required arguments (right_pin, enough_money) and 1 optional argument (above_daily_limit) and the test called it with 4 arguments (right_pin, enough_money, above_daily_limit and card_expired)
GREEN: make it pass
I add
card_expiredto the function signature inatm.py1def withdraw( 2 right_pin, enough_money, 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.py84# Exceptions seen 85# AssertionError 86# NameError 87# AttributeError 88# TypeError 89# SyntaxErrorI add a default value for
card_expiredinatm.py1def withdraw( 2 right_pin, enough_money, 3 above_daily_limit=False, card_expired=False, 4 ):the test passes.
REFACTOR: make it better
I add an assertion for when the right PIN is entered, there is enough money in the account, it is above limit for daily withdrawals, and the card has NOT expired
PIN
money
daily limit
card expired
withdrawal
right PIN
enough money
above limit
NOT expired
DENIED
10 def test_right_pin_enough_money_w_limit(self): 11 self.assertEqual( 12 src.atm.withdraw( 13 right_pin=True, 14 enough_money=True, 15 above_daily_limit=True, 16 card_expired=True, 17 ), 18 DENIED 19 ) 20 21 self.assertEqual( 22 src.atm.withdraw( 23 right_pin=True, 24 enough_money=True, 25 above_daily_limit=True, 26 card_expired=False, 27 ), 28 DENIED 29 ) 30 31 self.assertEqual( 32 src.atm.withdraw( 33 right_pin=True, 34 enough_money=True, 35 ), 36 'CASH' 37 )the test is still green. I add a value for the
card_expiredparameter to make it clearer, even though it is the default valueI add an assertion for when the right PIN is entered, there is enough money in the account, it is NOT above limit for daily withdrawals, and the card has expired
PIN
money
daily limit
card expired
withdrawal
right PIN
enough money
NOT above limit
expired
DENIED
10 def test_right_pin_enough_money_w_limit(self): 11 self.assertEqual( 12 src.atm.withdraw( 13 right_pin=True, 14 enough_money=True, 15 above_daily_limit=True, 16 card_expired=True, 17 ), 18 DENIED 19 ) 20 21 self.assertEqual( 22 src.atm.withdraw( 23 right_pin=True, 24 enough_money=True, 25 above_daily_limit=True, 26 card_expired=False, 27 ), 28 DENIED 29 ) 30 31 self.assertEqual( 32 src.atm.withdraw( 33 right_pin=True, 34 enough_money=True, 35 above_daily_limit=False, 36 card_expired=True, 37 ), 38 DENIED 39 ) 40 41 self.assertEqual( 42 src.atm.withdraw( 43 right_pin=True, 44 enough_money=True, 45 ), 46 'CASH' 47 ) 48 49 def test_right_pin_not_enough_money_w_limit(self):the terminal is my friend, and shows AssertionError
AssertionError: 'CASH' != 'DENIED'because the
withdrawfunction gives me ‘CASH’ and the assertion expects ‘DENIED’I add an if statement to the
withdrawfunction inatm.py1def withdraw( 2 right_pin, enough_money, 3 above_daily_limit=False, card_expired=False, 4 ): 5 denied = 'DENIED' 6 7 if card_expired == True: 8 return denied 9 10 if above_daily_limit: 11 return denied 12 13 if not right_pin: 14 return denied 15 16 if not enough_money: 17 return denied 18 19 return 'CASH'the test passes.
I use the bool built-in function
7 # if card_expired == True: 8 if bool(card_expired) == True: 9 return deniedthe test is still green.
I remove
== True7 # if card_expired == True: 8 # if bool(card_expired) == True: 9 if bool(card_expired): 10 return deniedstill green.
I remove bool
7 # if card_expired == True: 8 # if bool(card_expired) == True: 9 # if bool(card_expired): 10 if card_expired: 11 return deniedgreen, because
if bool(something) == Trueis the same asif bool(something)is the same asif somethingI remove the commented lines
1def withdraw( 2 right_pin, enough_money, 3 above_daily_limit=False, card_expired=False, 4 ): 5 denied = 'DENIED' 6 7 if card_expired: 8 return denied 9 10 if above_daily_limit: 11 return denied 12 13 if not right_pin: 14 return denied 15 16 if not enough_money: 17 return denied 18 19 return 'CASH'this is what happens when the
withdrawfunction is calledit 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 money in the account
it gives me ‘CASH’ if the above conditions are NOT met
I add values for the
above_daily_limitandcard_expiredparameters to make it clearer even though they have default values, in the next assertion in test_right_pin_enough_money_w_limit for the case where the right PIN is entered, there is enough money in the account, it is NOT above limit for daily withdrawals, and the card has NOT expired, intest_atm.pyPIN
money
daily limit
card expired
withdrawal
right PIN
enough money
NOT above limit
NOT expired
CASH
10 def test_right_pin_enough_money_w_limit(self): 11 self.assertEqual( 12 src.atm.withdraw( 13 right_pin=True, 14 enough_money=True, 15 above_daily_limit=True, 16 card_expired=True, 17 ), 18 DENIED 19 ) 20 21 self.assertEqual( 22 src.atm.withdraw( 23 right_pin=True, 24 enough_money=True, 25 above_daily_limit=True, 26 card_expired=False, 27 ), 28 DENIED 29 ) 30 31 self.assertEqual( 32 src.atm.withdraw( 33 right_pin=True, 34 enough_money=True, 35 above_daily_limit=False, 36 card_expired=True, 37 ), 38 DENIED 39 ) 40 41 self.assertEqual( 42 src.atm.withdraw( 43 right_pin=True, 44 enough_money=True, 45 above_daily_limit=False, 46 card_expired=False, 47 ), 48 'CASH' 49 ) 50 51 def test_right_pin_not_enough_money_w_limit(self):still green.
I change the name of the test from test_right_pin_enough_money_w_limit to test_right_pin_enough_money_w_card
8class TestATM(unittest.TestCase): 9 10 def test_right_pin_enough_money_w_card(self): 11 self.assertEqual( 12 src.atm.withdraw( 13 right_pin=True, 14 enough_money=True, 15 above_daily_limit=True, 16 card_expired=True, 17 ), 18 DENIED 19 )
test_right_pin_not_enough_money_w_card
The truth table for if the right PIN is entered AND there is NOT enough money in the account, is
PIN |
money |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
NOT enough money |
above limit |
expired |
DENIED |
right PIN |
NOT enough money |
above limit |
NOT expired |
DENIED |
right PIN |
NOT enough money |
NOT above limit |
expired |
DENIED |
right PIN |
NOT enough money |
NOT above limit |
NOT expired |
DENIED |
I add a value for the
card_expiredparameter to the first assertion in test_right_pin_not_enough_money_w_limit for when the right PIN is entered, there is NOT enough money in the account, it is above limit for daily withdrawals, and the card has expiredPIN
money
daily limit
card expired
withdrawal
right PIN
NOT enough money
above limit
expired
DENIED
51 def test_right_pin_not_enough_money_w_limit(self): 52 self.assertEqual( 53 src.atm.withdraw( 54 right_pin=True, 55 enough_money=False, 56 above_daily_limit=True, 57 card_expired=True, 58 ), 59 DENIED 60 )the test is still green.
I add an assertion for when the right PIN is entered, there is NOT enough money in the account, it is above limit for daily withdrawals, and the card has NOT expired
PIN
money
daily limit
card expired
withdrawal
right PIN
NOT enough money
above limit
NOT expired
DENIED
51 def test_right_pin_not_enough_money_w_limit(self): 52 self.assertEqual( 53 src.atm.withdraw( 54 right_pin=True, 55 enough_money=False, 56 above_daily_limit=True, 57 card_expired=True, 58 ), 59 DENIED 60 ) 61 62 self.assertEqual( 63 src.atm.withdraw( 64 right_pin=True, 65 enough_money=False, 66 above_daily_limit=True, 67 card_expired=False, 68 ), 69 DENIED 70 ) 71 72 self.assertEqual( 73 src.atm.withdraw( 74 right_pin=True, 75 enough_money=False, 76 ), 77 DENIED 78 ) 79 80 def test_wrong_pin_enough_money_w_limit(self):still green.
I add an assertion for when the right PIN is entered, there is NOT enough money in the account, it is NOT above limit for daily withdrawals, and the card has expired
PIN
money
daily limit
card expired
withdrawal
right PIN
NOT enough money
NOT above limit
expired
DENIED
51 def test_right_pin_not_enough_money_w_limit(self): 52 self.assertEqual( 53 src.atm.withdraw( 54 right_pin=True, 55 enough_money=False, 56 above_daily_limit=True, 57 card_expired=True, 58 ), 59 DENIED 60 ) 61 62 self.assertEqual( 63 src.atm.withdraw( 64 right_pin=True, 65 enough_money=False, 66 above_daily_limit=True, 67 card_expired=False, 68 ), 69 DENIED 70 ) 71 72 self.assertEqual( 73 src.atm.withdraw( 74 right_pin=True, 75 enough_money=False, 76 above_daily_limit=False, 77 card_expired=True, 78 ), 79 DENIED 80 ) 81 82 self.assertEqual( 83 src.atm.withdraw( 84 right_pin=True, 85 enough_money=False, 86 ), 87 DENIED 88 ) 89 90 def test_wrong_pin_enough_money_w_limit(self):green.
I add values for the
above_daily_limitandcard_expiredparameters to the assertion for when the right PIN is entered, there is NOT enough money in the account, it is NOT above limit for daily withdrawals, and the card has NOT expiredPIN
money
daily limit
card expired
withdrawal
right PIN
NOT enough money
NOT above limit
NOT expired
DENIED
7 def test_right_pin_not_enough_money_w_limit(self): 8 self.assertEqual( 9 src.atm.withdraw( 10 right_pin=True, 11 enough_money=False, 12 above_daily_limit=True, 13 card_expired=True, 14 ), 15 DENIED 16 ) 17 18 self.assertEqual( 19 src.atm.withdraw( 20 right_pin=True, 21 enough_money=False, 22 above_daily_limit=True, 23 card_expired=False, 24 ), 25 DENIED 26 ) 27 28 self.assertEqual( 29 src.atm.withdraw( 30 right_pin=True, 31 enough_money=False, 32 above_daily_limit=False, 33 card_expired=True, 34 ), 35 DENIED 36 ) 37 38 self.assertEqual( 39 src.atm.withdraw( 40 right_pin=True, 41 enough_money=False, 42 above_daily_limit=False, 43 card_expired=True, 44 ), 45 DENIED 46 ) 47 48 def test_wrong_pin_enough_money_w_limit(self):still green.
I change the name of the test from test_right_pin_not_enough_money_w_limit to test_right_pin_not_enough_money_w_card
41 self.assertEqual( 42 src.atm.withdraw( 43 right_pin=True, 44 enough_money=True, 45 above_daily_limit=False, 46 card_expired=False, 47 ), 48 'CASH' 49 ) 50 51 def test_right_pin_not_enough_money_w_card(self): 52 self.assertEqual( 53 src.atm.withdraw( 54 right_pin=True, 55 enough_money=False, 56 above_daily_limit=True, 57 card_expired=True, 58 ), 59 DENIED 60 )
test_wrong_pin_enough_money_w_card
The truth table for if the wrong PIN is entered, and there is enough money in the account, is
PIN |
money |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
enough money |
above limit |
expired |
DENIED |
wrong PIN |
enough money |
above limit |
NOT expired |
DENIED |
wrong PIN |
enough money |
NOT above limit |
expired |
DENIED |
wrong PIN |
enough money |
NOT above limit |
NOT expired |
DENIED |
I add a value for the
card_expiredparameter to the assertion in test_wrong_pin_enough_money_w_limit for the case where the wrong PIN is entered, there is enough money in the account, it is above limit for daily withdrawals, and the card has expiredPIN
money
daily limit
card expired
withdrawal
wrong PIN
enough money
above limit
expired
DENIED
92 def test_wrong_pin_enough_money_w_limit(self): 93 self.assertEqual( 94 src.atm.withdraw( 95 right_pin=False, 96 enough_money=True, 97 above_daily_limit=True, 98 card_expired=True, 99 ), 100 DENIED 101 )the test is still green.
I add an assertion for the case where the wrong PIN is entered, there is enough money in the account, it is above limit for daily withdrawals, and the card has NOT expired
PIN
money
daily limit
card expired
withdrawal
wrong PIN
enough money
above limit
NOT expired
DENIED
92 def test_wrong_pin_enough_money_w_limit(self): 93 self.assertEqual( 94 src.atm.withdraw( 95 right_pin=False, 96 enough_money=True, 97 above_daily_limit=True, 98 card_expired=True, 99 ), 100 DENIED 101 ) 102 103 self.assertEqual( 104 src.atm.withdraw( 105 right_pin=False, 106 enough_money=True, 107 above_daily_limit=True, 108 card_expired=False, 109 ), 110 DENIED 111 ) 112 113 self.assertEqual( 114 src.atm.withdraw( 115 right_pin=False, 116 enough_money=True, 117 ), 118 DENIED 119 ) 120 121 def test_wrong_pin_not_enough_money_w_limit(self):still green.
I add an assertion for when the wrong PIN is entered, there is enough money in the account, it is NOT above limit for daily withdrawals, and the card has expired
PIN
money
daily limit
card expired
withdrawal
wrong PIN
enough money
NOT above limit
expired
DENIED
92 def test_wrong_pin_enough_money_w_limit(self): 93 self.assertEqual( 94 src.atm.withdraw( 95 right_pin=False, 96 enough_money=True, 97 above_daily_limit=True, 98 card_expired=True, 99 ), 100 DENIED 101 ) 102 103 self.assertEqual( 104 src.atm.withdraw( 105 right_pin=False, 106 enough_money=True, 107 above_daily_limit=True, 108 card_expired=False, 109 ), 110 DENIED 111 ) 112 113 self.assertEqual( 114 src.atm.withdraw( 115 right_pin=False, 116 enough_money=True, 117 above_daily_limit=False, 118 card_expired=True, 119 ), 120 DENIED 121 ) 122 123 self.assertEqual( 124 src.atm.withdraw( 125 right_pin=False, 126 enough_money=True, 127 ), 128 DENIED 129 ) 130 131 def test_wrong_pin_not_enough_money_w_limit(self):still green.
I add values for the
card_expiredandabove_daily_limitparameters to the last assertion to make it clearer, for when the wrong PIN is entered, there is enough money in the account, it is NOT above limit for daily withdrawals, and the card has NOT expiredPIN
money
daily limit
card expired
withdrawal
wrong PIN
enough money
NOT above limit
NOT expired
DENIED
92 def test_wrong_pin_enough_money_w_limit(self): 93 self.assertEqual( 94 src.atm.withdraw( 95 right_pin=False, 96 enough_money=True, 97 above_daily_limit=True, 98 card_expired=True, 99 ), 100 DENIED 101 ) 102 103 self.assertEqual( 104 src.atm.withdraw( 105 right_pin=False, 106 enough_money=True, 107 above_daily_limit=True, 108 card_expired=False, 109 ), 110 DENIED 111 ) 112 113 self.assertEqual( 114 src.atm.withdraw( 115 right_pin=False, 116 enough_money=True, 117 above_daily_limit=False, 118 card_expired=True, 119 ), 120 DENIED 121 ) 122 123 self.assertEqual( 124 src.atm.withdraw( 125 right_pin=False, 126 enough_money=True, 127 above_daily_limit=False, 128 card_expired=False, 129 ), 130 DENIED 131 ) 132 133 def test_wrong_pin_not_enough_money_w_limit(self):the test is still green.
I change the name of test_wrong_pin_enough_money_w_limit to test_wrong_pin_enough_money_w_card
82 self.assertEqual( 83 src.atm.withdraw( 84 right_pin=True, 85 enough_money=False, 86 above_daily_limit=False, 87 card_expired=True, 88 ), 89 DENIED 90 ) 91 92 def test_wrong_pin_enough_money_w_card(self): 93 self.assertEqual( 94 src.atm.withdraw( 95 right_pin=False, 96 enough_money=True, 97 above_daily_limit=True, 98 card_expired=True, 99 ), 100 DENIED 101 )
test_wrong_pin_not_enough_money_w_card
The truth table for if the wrong PIN is entered, and there is NOT enough money in the account, is
PIN |
money |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
NOT enough money |
above limit |
expired |
DENIED |
wrong PIN |
NOT enough money |
above limit |
NOT expired |
DENIED |
wrong PIN |
NOT enough money |
NOT above limit |
expired |
DENIED |
wrong PIN |
NOT enough money |
NOT above limit |
NOT expired |
DENIED |
I add a value for the
card_expiredparameter to the first assertion in test_wrong_pin_not_enough_money_w_limit for when the wrong PIN is entered, there is NOT enough money in the account, it is above limit for daily withdrawals, and the card has expiredPIN
money
daily limit
card expired
withdrawal
wrong PIN
NOT enough money
above limit
expired
DENIED
133 def test_wrong_pin_not_enough_money_w_limit(self): 134 self.assertEqual( 135 src.atm.withdraw( 136 right_pin=False, 137 enough_money=False, 138 above_daily_limit=True, 139 card_expired=True, 140 ), 141 DENIED 142 )still green.
I add an assertion, for when the wrong PIN is entered, there is NOT enough money in the account, it is above limit for daily withdrawals, and the card has NOT expired
PIN
money
daily limit
card expired
withdrawal
wrong PIN
NOT enough money
above limit
NOT expired
DENIED
113 def test_wrong_pin_not_enough_money_w_limit(self): 114 self.assertEqual( 115 src.atm.withdraw( 116 right_pin=False, 117 enough_money=False, 118 above_daily_limit=True, 119 card_expired=True, 120 ), 121 DENIED 122 ) 123 124 self.assertEqual( 125 src.atm.withdraw( 126 right_pin=False, 127 enough_money=False, 128 above_daily_limit=True, 129 card_expired=False, 130 ), 131 DENIED 132 ) 133 134 self.assertEqual( 135 src.atm.withdraw( 136 right_pin=False, 137 enough_money=False, 138 ), 139 DENIED 140 ) 141 142 143# Exceptions seengreen.
I add an assertion for when the wrong PIN is entered, there is NOT enough money in the account, it is NOT above limit for daily withdrawals and the card has expired
PIN
money
daily limit
card expired
withdrawal
wrong PIN
NOT enough money
NOT above limit
expired
DENIED
133 def test_wrong_pin_not_enough_money_w_limit(self): 134 self.assertEqual( 135 src.atm.withdraw( 136 right_pin=False, 137 enough_money=False, 138 above_daily_limit=True, 139 card_expired=True, 140 ), 141 DENIED 142 ) 143 144 self.assertEqual( 145 src.atm.withdraw( 146 right_pin=False, 147 enough_money=False, 148 above_daily_limit=True, 149 card_expired=False, 150 ), 151 DENIED 152 ) 153 154 self.assertEqual( 155 src.atm.withdraw( 156 right_pin=False, 157 enough_money=False, 158 above_daily_limit=False, 159 card_expired=True, 160 ), 161 DENIED 162 ) 163 164 self.assertEqual( 165 src.atm.withdraw( 166 right_pin=False, 167 enough_money=False, 168 ), 169 DENIED 170 ) 171 172 173# Exceptions seenstill green.
I add values for the
above_daily_limitandcard_expiredparameters to make it clearer, for when the wrong PIN is entered, there is NOT enough money in the account, it is NOT above limit for daily withdrawals, and the card has NOT expiredPIN
money
daily limit
card expired
withdrawal
wrong PIN
NOT enough money
NOT above limit
NOT expired
DENIED
133 def test_wrong_pin_not_enough_money_w_limit(self): 134 self.assertEqual( 135 src.atm.withdraw( 136 right_pin=False, 137 enough_money=False, 138 above_daily_limit=True, 139 card_expired=True, 140 ), 141 DENIED 142 ) 143 144 self.assertEqual( 145 src.atm.withdraw( 146 right_pin=False, 147 enough_money=False, 148 above_daily_limit=True, 149 card_expired=False, 150 ), 151 DENIED 152 ) 153 154 self.assertEqual( 155 src.atm.withdraw( 156 right_pin=False, 157 enough_money=False, 158 above_daily_limit=False, 159 card_expired=True, 160 ), 161 DENIED 162 ) 163 164 self.assertEqual( 165 src.atm.withdraw( 166 right_pin=False, 167 enough_money=False, 168 above_daily_limit=False, 169 card_expired=False, 170 ), 171 DENIED 172 ) 173 174 175# Exceptions seenall the tests are green.
The withdraw function can be written with Logical Disjunction (OR) or Logical Conjunction (AND). Try it and see which one you like better?
close the project
I close
test_atm.pyandatm.pyin the editorI click in the terminal where the tests are running, then use q on the keyboard to leave the tests. The terminal goes back to the command line.
I change directory to the parent of
atmcd ..the terminal shows
.../pumping_pythonI am back in the
pumping_pythondirectory
review
I ran tests for an Automated Teller Machine with these inputs:
has the card expired?
is the PIN correct?
is the amount I want to take, smaller or bigger than what is in the account?
will the withdrawal put the account above or below the daily limit for withdrawals?
the inputs gave me this truth table
PIN |
money |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
enough money |
above limit |
expired |
DENIED |
right PIN |
enough money |
above limit |
NOT expired |
DENIED |
right PIN |
enough money |
NOT above limit |
expired |
DENIED |
right PIN |
enough money |
NOT above limit |
NOT expired |
CASH |
PIN |
money |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
NOT enough money |
above limit |
expired |
DENIED |
right PIN |
NOT enough money |
above limit |
NOT expired |
DENIED |
right PIN |
NOT enough money |
NOT above limit |
expired |
DENIED |
right PIN |
NOT enough money |
NOT above limit |
NOT expired |
DENIED |
PIN |
money |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
enough money |
above limit |
expired |
DENIED |
wrong PIN |
enough money |
above limit |
NOT expired |
DENIED |
wrong PIN |
enough money |
NOT above limit |
expired |
DENIED |
wrong PIN |
enough money |
NOT above limit |
NOT expired |
DENIED |
PIN |
money |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
NOT enough money |
above limit |
expired |
DENIED |
wrong PIN |
NOT enough money |
above limit |
NOT expired |
DENIED |
wrong PIN |
NOT enough money |
NOT above limit |
expired |
DENIED |
wrong PIN |
NOT enough money |
NOT above limit |
NOT expired |
DENIED |
The ATM only gives me 'CASH' when the right PIN is entered, there is enough money 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 |
money |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
enough money |
above limit |
expired |
DENIED: Card Expired |
right PIN |
enough money |
above limit |
NOT expired |
DENIED: You have exceeded the daily withdrawal limit |
right PIN |
enough money |
NOT above limit |
expired |
DENIED: Card Expired |
right PIN |
enough money |
NOT above limit |
NOT expired |
CASH |
PIN |
money |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
right PIN |
NOT enough money |
above limit |
expired |
DENIED: Card Expired |
right PIN |
NOT enough money |
above limit |
NOT expired |
DENIED: Insufficient Funds |
right PIN |
NOT enough money |
NOT above limit |
expired |
DENIED: Card Expired |
right PIN |
NOT enough money |
NOT above limit |
NOT expired |
DENIED: Insufficient Funds |
PIN |
money |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
enough money |
above limit |
expired |
DENIED: You entered the wrong PIN. Try again… |
wrong PIN |
enough money |
above limit |
NOT expired |
DENIED: You entered the wrong PIN. Try again… |
wrong PIN |
enough money |
NOT above limit |
expired |
DENIED: You entered the wrong PIN. Try again… |
wrong PIN |
enough money |
NOT above limit |
NOT expired |
DENIED: You entered the wrong PIN. Try again… |
PIN |
money |
daily limit |
card expired |
withdrawal |
|---|---|---|---|---|
wrong PIN |
NOT enough money |
above limit |
expired |
DENIED: You entered the wrong PIN. Try again… |
wrong PIN |
NOT enough money |
above limit |
NOT expired |
DENIED: You entered the wrong PIN. Try again… |
wrong PIN |
NOT enough money |
NOT above limit |
expired |
DENIED: You entered the wrong PIN. Try again… |
wrong PIN |
NOT enough money |
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?
you know
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.