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

atm/tests/test_atm.py
 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        )
atm/tests/test_atm.py
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        )
atm/tests/test_atm.py
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        )
atm/tests/test_atm.py
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        )
atm/tests/test_atm.py
 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        )
atm/tests/test_atm.py
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        )
atm/tests/test_atm.py
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        )
atm/tests/test_atm.py
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.sh

    • I change the name of the project to atm in makePythonTdd.sh

       1#!/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 . --now
      
    • I run makePythonTdd.sh in the terminal to make the atm project

      ./makePythonTdd.sh
      
    • I open makePythonTdd.ps1

    • I change the name of the project to atm in makePythonTdd.ps1

       1uv 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 . --now
      
    • I run makePythonTdd.ps1 in the terminal to make the atm project

      .\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:7 to open it

  • I change assertFalse to assertTrue in test_atm.py

     4class TestATM(unittest.TestCase):
     5
     6    def test_failure(self):
     7        # self.assertFalse(True)
     8        self.assertTrue(True)
     9
    10
    11# Exceptions seen
    

    the test passes.

  • I open a new terminal then change directory to atm

    cd atm
    
  • I 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# NameError
    
  • I add an import statement at the top of the file so that I can test atm.py from the src folder

    1import 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__.py in the src folder does not have anything named withdraw in it.

  • I add AttributeError to the list of Exceptions seen

    16# Exceptions seen
    17# AssertionError
    18# NameError
    19# AttributeError
    
  • I open atm/__init__.py from the src folder

  • I delete all the text in the file then add a function named withdraw to atm.py

    1def withdraw():
    2    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: withdraw() got
               an unexpected keyword argument 'right_pin'
    

    because the test called the withdraw function 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.py

    16# Exceptions seen
    17# AssertionError
    18# NameError
    19# AttributeError
    20# TypeError
    
  • I add right_pin to the function definition in atm.py

    1def withdraw(right_pin):
    2    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: withdraw() got
               an unexpected keyword argument 'enough_cash'
    

    because the test called the withdraw function with a name (enough_cash) that is not in the parentheses of its definition.

  • I add enough_cash to the function signature

    1def withdraw(right_pin, enough_cash):
    2    return None
    

    the terminal is my friend, and shows AssertionError

    AssertionError: None != 'CASH'
    

    the withdraw function 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 seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'CASH' != 'DENIED'
    

    because the withdraw function 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 == 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    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) == False is the same as if not bool(something) == True is the same as if not something.

  • I remove the commented lines from the withdraw function

    1def 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.py

    PIN

    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 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 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 == 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    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) == False is the same as if not bool(something) == True is the same as if not something.

  • I remove the commented lines from the withdraw function

    1def 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.py

    PIN

    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 seen
    

    the 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 seen
    

    the 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.py

    1import src.atm
    2import unittest
    3
    4
    5DENIED = 'DENIED'
    6
    7
    8class TestATM(unittest.TestCase):
    
  • I use the new global variable for my_expectation in test_right_pin_not_enough_cash

    18    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_expectation in test_wrong_pin_enough_cash

    25    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_expectation in test_wrong_pin_not_enough_cash

    32    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 seen
    

    green.

  • 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 seen
    
  • I 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 withdraw function 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_limit to the function in atm.py

    1def 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_limit parameter to make it a choice

    1def 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 withdraw function 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 == True

    1def 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 == True is the same as if bool(something) == True is the same as if bool(something) is the same as if 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 withdraw function

     1def 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_limit parameter of the withdraw function is False

    PIN

    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_limit parameter 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_limit parameter because the default value for the above_daily_limit parameter of the withdraw function is False. This means that

    src.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_limit parameter 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 withdrawals

    PIN

    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_limit parameter in the call to src.atm.withdraw because the default value for the above_daily_limit parameter of the withdraw function is False. This means that

    src.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_limit to the call to src.atm.withdraw from 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 withdrawals

    PIN

    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 seen
    

    the 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 seen
    

    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_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 seen
    

    the 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_limit parameter in the call to src.atm.withdraw because the default value for the above_daily_limit parameter of the withdraw function is False. This means that

    reality = 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.withdraw directly in the assertion of 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        self.assertEqual(
    71            src.atm.withdraw(
    72                right_pin=False,
    73                enough_cash=False,
    74            ),
    75            DENIED
    76        )
    77
    78
    79# Exceptions seen
    

    the 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 seen
    
  • I call src.atm.withdraw directly in the assertion of test_above_limit_wrong_pin_not_enough_cash

    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        # )
    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.withdraw directly in the assertion of 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        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.withdraw directly in the assertion of test_above_limit_wrong_pin_enough_cash

    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        # )
    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.withdraw directly in the assertion of 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        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.withdraw directly in the assertion of test_above_limit_right_pin_not_enough_cash

    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        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.withdraw directly in the assertion of test_above_limit_right_pin_enough_cash

    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        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.withdraw directly and use ‘CASH’ for my_expectation in the assertion of test_below_limit_right_pin_not_enough_cash

    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(
    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_expired parameter to the call to the withdraw function 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_cash

    PIN

    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 withdraw function with a name (card_expired) that is not in the parentheses of its definition.


GREEN: make it pass


  • I add card_expired to the function signature in atm.py

    1def 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 default
    

    because parameters without default values must come before parameters with default values.

  • I add SyntaxError to the list of Exceptions seen, in test_atm.py

    88# Exceptions seen
    89# AssertionError
    90# NameError
    91# AttributeError
    92# TypeError
    93# SyntaxError
    
  • I add a default value for card_expired in atm.py

    1def 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_limit parameter to test_below_limit_right_pin_enough_cash to make the assertion clearer, in test_atm.py

    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
    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 withdraw function in atm.py

    1def 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 denied
    

    the 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 == True

     1def 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.py

     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        )
    
  • 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_expired parameter to the call to the withdraw function 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_cash

    PIN

    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_expired parameter to the call to the withdraw function 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 expired

    PIN

    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_expired and above_daily_limit parameters to the call to the withdraw function 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_cash

    PIN

    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_expired parameter 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 expired

    PIN

    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_expired and above_daily_limit parameters 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 expired

    PIN

    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_expired parameter 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 expired

    PIN

    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_expired and above_daily_limit parameters 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 expired

    PIN

    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 seen
    

    the 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 seen
    

    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_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

  • I close test_atm.py and atm.py

  • I click in the terminal where the tests are running

  • I use q on the keyboard to leave the tests. The terminal shows

    .../pumping_python
    

    I am back in the pumping_python directory.


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

Do you want to see all the CODE I typed in this chapter?


what is next?

Would you like to test making a Car?


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.