telephone

Part of Computer Programming is sending input data to a process and getting output data back

input_data -> process -> output_data

I send things (input data) to a program to test it, and check if what I think will happen (my expectation) is the same as the results I get (reality). This helps me answer two questions:

  • what is the same?

  • what is different?

The difference helps me know what to change to get what I want. I do this with the assertEqual method, which takes 2 inputs and checks if they are the same

self.assertEqual(reality, my_expectation)

where

  • reality is what happens when I do something with code

  • my_expectation is what I think will happen when I do something with code

The exercises in this chapter to show how I can pass input data from a test to a function in a module.


preview

These are the tests I have by the end of the chapter

 1import src.telephone
 2import unittest
 3
 4
 5class TestTelephone(unittest.TestCase):
 6
 7    def test_passing_a_string(self):
 8        reality = src.telephone.text('hello')
 9        my_expectation = 'I got: hello'
10        self.assertEqual(reality, my_expectation)
11
12        reality = src.telephone.text('yes')
13        my_expectation = 'I got: yes'
14        self.assertEqual(reality, my_expectation)
15
16    def test_passing_none(self):
17        reality = src.telephone.text(None)
18        my_expectation = 'I got: None'
19        self.assertEqual(reality, my_expectation)
20
21    def test_passing_booleans(self):
22        reality = src.telephone.text(False)
23        my_expectation = 'I got: False'
24        self.assertEqual(reality, my_expectation)
25
26        reality = src.telephone.text(True)
27        my_expectation = 'I got: True'
28        self.assertEqual(reality, my_expectation)
29
30    def test_passing_an_integer(self):
31        reality = src.telephone.text(1234)
32        my_expectation = 'I got: 1234'
33        self.assertEqual(reality, my_expectation)
34
35    def test_passing_a_float(self):
36        reality = src.telephone.text(1.234)
37        my_expectation = 'I got: 1.234'
38        self.assertEqual(reality, my_expectation)
39
40    def test_passing_a_tuple(self):
41        reality = src.telephone.text((1, 2, 3, 'n'))
42        my_expectation = "I got: (1, 2, 3, 'n')"
43        self.assertEqual(reality, my_expectation)
44
45    def test_passing_a_list(self):
46        reality = src.telephone.text([1, 2, 3, "n"])
47        my_expectation = "I got: [1, 2, 3, 'n']"
48        self.assertEqual(reality, my_expectation)
49
50    def test_passing_a_dictionary(self):
51        reality = src.telephone.text(
52            {
53                'key1': 'value1',
54                'keyN': [0, 1, 2, 'n'],
55            }
56        )
57        my_expectation = (
58            "I got: "
59            "{'key1': 'value1', 'keyN': [0, 1, 2, 'n']}"
60        )
61        self.assertEqual(reality, my_expectation)
62
63    def test_passing_a_class(self):
64        reality = src.telephone.text(object)
65        my_expectation = "I got: <class 'object'>"
66        self.assertEqual(reality, my_expectation)
67
68        reality = src.telephone.text(TestTelephone)
69        my_expectation = (
70            "I got: <class "
71            "'tests.test_telephone.TestTelephone'>"
72        )
73        self.assertEqual(reality, my_expectation)
74
75
76# Exceptions seen
77# AssertionError
78# NameError
79# AttributeError
80# TypeError

start the project

  • I name this project telephone

  • I open a terminal

  • I use uv to make a directory for the project and initialize it

    uv init telephone
    

    the terminal shows

    Initialized project `telephone`
    at `.../pumping_python/telephone`
    

    then goes back to the command line.

  • I change directory to the project

    cd telephone
    

    the terminal shows I am in the telephone folder

    .../pumping_python/telephone
    
  • I make a directory for the source code

    mkdir src
    

    the terminal goes back to the command line.

  • I use the mv program to change the name of main.py to telephone.py and move it to the src folder

    mv main.py src/telephone.py
    
    Move-Item main.py src/telephone.py
    

    the terminal goes back to the command line.

  • I make a directory for the tests

    mkdir tests
    

    the terminal goes back to the command line.

  • I make the tests directory a Python package

    Danger

    use 2 underscores (__) before and after init for __init__.py not _init_.py

    touch tests/__init__.py
    
    New-Item tests/__init__.py
    

    the terminal goes back to the command line.

  • I make a Python file for the tests in the tests directory

    touch tests/test_telephone.py
    
    New-Item tests/test_telephone.py
    

    the terminal goes back to the command line.

  • I open test_telephone.py in the editor of the Integrated Development Environment (IDE)

    Tip

    I can open a file from the terminal in the Integrated Development Environment (IDE) with the name of the program and the name of the file. That means if I type this in the terminal

    code tests/test_telephone.py
    

    Visual Studio Code opens test_telephone.py in the editor

  • I add the first failing test to test_telephone.py

    1import unittest
    2
    3
    4class TestTelephone(unittest.TestCase):
    5
    6    def test_failure(self):
    7        self.assertFalse(True)
    
  • I go back to the terminal to make a requirements file for the Python packages I need

    echo "pytest" > requirements.txt
    

    the terminal goes back to the command line.

  • I add pytest-watcher to the requirements file

    echo "pytest-watcher" >> requirements.txt
    

    the terminal goes back to the command line.

  • I install the Python packages that I wrote in the requirements file

    uv add --requirement requirements.txt
    

    the terminal shows that it installed the Python packages

  • I add the new files and folders to git for tracking

    git add .
    

    the terminal goes back to the command line.

  • I add a git commit message

    git commit --all --message 'initialize project'
    

    the terminal shows

    [main (root-commit) a0b12c3] initialize project
     9 files changed, 148 insertions(+)
     create mode 100644 .gitignore
     create mode 100644 .python-version
     create mode 100644 README.md
     create mode 100644 pyproject.toml
     create mode 100644 requirements.txt
     create mode 100644 src/telephone.py
     create mode 100644 tests/__init__.py
     create mode 100644 tests/test_telephone.py
     create mode 100644 uv.lock
    

    then goes back to the command line.

  • I use pytest-watcher to run the tests automatically

    uv run pytest-watcher . --now
    

    the terminal is my friend, and shows AssertionError

    ================================ FAILURES ================================
    ______________________ TestTelephone.test_failure ________________________
    
    self = <tests.test_telephone.TestTelephone testMethod=test_failure>
    
        def test_failure(self):
    >       self.assertFalse(True)
    E       AssertionError: True is not false
    
    tests/test_telephone.py:7: AssertionError
    ======================== short test summary info =========================
    FAILED tests/test_telephone.py::TestTelephone::test_failure - AssertionError: True is not false
    =========================== 1 failed in X.YZs ============================
    

    because True is NOT False

    if the terminal does not show the same error, then check

    • if your tests/__init__.py has two underscores (__) before and after init for __init__.py not _init_.py

    • if you ran echo "pytest-watcher" >> requirements.txt, to add pytest-watcher to the requirements file

    fix those errors and try to run uv run pytest-watcher . --now again

  • I add AssertionError to the list of Exceptions seen in test_telephone.py in the editor

     4class TestTelephone(unittest.TestCase):
     5
     6    def test_failure(self):
     7        self.assertFalse(True)
     8
     9
    10# Exceptions seen
    11# AssertionError
    
  • then I change True to False in the assertion

    7        self.assertFalse(False)
    

    the test passes.


test_passing_a_string

I can pass a string from a test to a function


RED: make it fail


I change test_failure to test_passing_a_string

 4class TestTelephone(unittest.TestCase):
 5
 6    def test_passing_a_string(self):
 7        reality = src.telephone.text('hello')
 8        my_expectation = 'I got: hello'
 9        self.assertEqual(reality, my_expectation)
10
11
12# Exceptions seen
13# AssertionError

the terminal is my friend, and shows NameError

NameError: name 'src' is not defined

because there is no definition for src in test_telephone.py


GREEN: make it pass


  • I add NameError to the list of Exceptions seen

    12# Exceptions seen
    13# AssertionError
    14# NameError
    
  • I add an import statement for the telephone module from the src folder, at the top of the file

    1import src.telephone
    2import unittest
    3
    4
    5class TestTelephone(unittest.TestCase):
    
  • I add AttributeError to the list of Exceptions seen in test_telephone.py

    13# Exceptions seen
    14# AssertionError
    15# NameError
    16# AttributeError
    
  • I use the Explorer to open telephone.py from the src folder in the editor

  • I delete the text, then add a name to telephone.py

    1text
    

    the terminal is my friend, and shows NameError

    NameError: name 'text' is not defined
    

    because the name is in the file, and I have not told Python what it means

  • I point text to None

    1text = None
    

    the terminal is my friend, and shows TypeError

    TypeError: 'NoneType' object is not callable
    

    because I cannot call None the way I can call a function

  • I add TypeError to the list of Exceptions seen in test_telephone.py

    13# Exceptions seen
    14# AssertionError
    15# NameError
    16# AttributeError
    17# TypeError
    
  • I change text to a function in telephone.py to make it callable

    1def text():
    2    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: text() takes 0 positional arguments
               but 1 was given
    

    because the definition for src.telephone.text does not allow calling it with inputs and the test sends 'hello' as input - the parentheses are empty.

  • I make the function take input

    1def text(the_input):
    2    return None
    
    • the_input is the name I used for the input, I can use any name I want.

    • the terminal is my friend, and shows AssertionError

      AssertionError: None != 'I got: hello'
      

      because the assertion expects 'I got: hello' and the text function returns None.

  • I copy the string from the terminal and paste it in the return statement to replace None

    1def text(the_input):
    2    return 'I got: hello'
    

    the test passes!


REFACTOR: make it better


The problem with this solution is that the text function does not care about what it gets, it always returns 'I got: hello' when called. I want it to return the value it gets as part of the message.

  • I add a new assertion to test_passing_a_string in test_telephone.py

     7    def test_passing_a_string(self):
     8        reality = src.telephone.text('hello')
     9        my_expectation = 'I got: hello'
    10        self.assertEqual(reality, my_expectation)
    11
    12        reality = src.telephone.text('yes')
    13        my_expectation = 'I got: yes'
    14        self.assertEqual(reality, my_expectation)
    15
    16
    17# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'I got: hello' != 'I got: yes'
    

    because the text function always returns 'I got: hello' and this assertion expects 'I got: yes'

  • I change the return statement in telephone.py to match

    1def text(the_input):
    2    return 'I got: yes'
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'I got: yes' != 'I got: hello'
    

    it did not work, my change broke the assertion that was passing before. The return statement has to use the input it gets as part of the output.


what is string interpolation?

  • I use an f-string which lets me add any variables I want to a string

    1def text(the_input):
    2    return f'I got: {the_input}'
    

    the test passes.

    This is called string interpolation, I can use it to put values in strings. A string is anything inside quotes e.g.

    • 'single quotes'

    • '''triple single quotes'''

    • "double quotes"

    • """triple double quotes"""

  • I add a variable to use the remove repetition of 'hello' from the test in test_telephone.py

     7    def test_passing_a_string(self):
     8        a_string = 'hello'
     9        reality = src.telephone.text('hello')
    10        my_expectation = 'I got: hello'
    11        self.assertEqual(reality, my_expectation)
    
  • I use the new variable and string interpolation to remove repetition of 'hello' from the test

     7    def test_passing_a_string(self):
     8        a_string = 'hello'
     9        # reality = src.telephone.text('hello')
    10        reality = src.telephone.text(a_string)
    11        # my_expectation = 'I got: hello'
    12        my_expectation = f'I got: {a_string}'
    13        self.assertEqual(reality, my_expectation)
    

    the test is still green.

  • I add a variable to use the remove repetition of 'yes' from the test

     7    def test_passing_a_string(self):
     8        a_string = 'hello'
     9        # reality = src.telephone.text('hello')
    10        reality = src.telephone.text(a_string)
    11        # my_expectation = 'I got: hello'
    12        my_expectation = f'I got: {a_string}'
    13        self.assertEqual(reality, my_expectation)
    14
    15        a_string = 'yes'
    16        reality = src.telephone.text('yes')
    17        my_expectation = 'I got: yes'
    18        self.assertEqual(reality, my_expectation)
    
  • I use the new variable and string interpolation to remove repetition of 'yes' from the test

    15        a_string = 'yes'
    16        # reality = src.telephone.text('yes')
    17        reality = src.telephone.text(a_string)
    18        # my_expectation = 'I got: yes'
    19        my_expectation = f'I got: {a_string}'
    20        self.assertEqual(reality, my_expectation)
    21
    22
    23# Exceptions seen
    

    still green.

  • I remove the commented lines

     7    def test_passing_a_string(self):
     8        a_string = 'hello'
     9        reality = src.telephone.text(a_string)
    10        my_expectation = f'I got: {a_string}'
    11        self.assertEqual(reality, my_expectation)
    12
    13        a_string = 'yes'
    14        reality = src.telephone.text(a_string)
    15        my_expectation = f'I got: {a_string}'
    16        self.assertEqual(reality, my_expectation)
    17
    18
    19# Exceptions seen
    
  • I open a new terminal then change directories to telephone

    cd telephone
    

    the terminal shows I am in the telephone folder

    .../pumping_python/telephone
    
  • I add a git commit message in the new terminal

    git commit -am 'add test_passing_a_string'
    

    the terminal shows a summary of the changes then goes back to the command line.

I can pass a string from a test to a function.


test_passing_none

I can pass None from a test to a function


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a failing test for None (the simplest Python data structure) to test_telephone.py

     7    def test_passing_a_string(self):
     8        a_string = 'hello'
     9        reality = src.telephone.text(a_string)
    10        my_expectation = f'I got: {a_string}'
    11        self.assertEqual(reality, my_expectation)
    12
    13        a_string = 'yes'
    14        reality = src.telephone.text(a_string)
    15        my_expectation = f'I got: {a_string}'
    16        self.assertEqual(reality, my_expectation)
    17
    18    def test_passing_none(self):
    19        reality = src.telephone.text(None)
    20        my_expectation = 'I got: "None"'
    21        self.assertEqual(reality, my_expectation)
    22
    23
    24# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'I got: None' != "I got: 'None'"
    

GREEN: make it pass


I remove the quotes around None in my_expectation

18    def test_passing_none(self):
19        reality = src.telephone.text(None)
20        my_expectation = 'I got: None'
21        self.assertEqual(reality, my_expectation)
22
23
24# Exceptions seen

the test passes.


REFACTOR: make it better


  • I add a variable to use to remove repetition of None

    18    def test_passing_none(self):
    19        none = None
    20        reality = src.telephone.text(None)
    21        my_expectation = 'I got: None'
    22        self.assertEqual(reality, my_expectation)
    23
    24
    25# Exceptions seen
    
  • I use the variable with an f-string to remove repetition of None

    18    def test_passing_none(self):
    19        none = None
    20        # reality = src.telephone.text(None)
    21        reality = src.telephone.text(none)
    22        # my_expectation = 'I got: None'
    23        my_expectation = f'I got: {none}'
    24        self.assertEqual(reality, my_expectation)
    25
    26
    27# Exceptions seen
    

    the test is still green.

  • I remove the comments

    18    def test_passing_none(self):
    19        none = None
    20        reality = src.telephone.text(none)
    21        my_expectation = f'I got: {none}'
    22        self.assertEqual(reality, my_expectation)
    23
    24
    25# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit --all --message 'add test_passing_none'
    

    the terminal shows a summary of the changes then goes back to the command line.

I can pass None from a test to a function.


test_passing_booleans

I can pass booleans from a test to a function


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test for booleans, first with an assertion for False

    18    def test_passing_none(self):
    19        none = None
    20        reality = src.telephone.text(none)
    21        my_expectation = f'I got: {none}'
    22        self.assertEqual(reality, my_expectation)
    23
    24    def test_passing_booleans(self):
    25        reality = src.telephone.text(False)
    26        my_expectation = 'I got: "False"'
    27        self.assertEqual(reality, my_expectation)
    28
    29
    30# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'I got: False' != 'I got: "False"'
    

GREEN: make it pass


I remove the quotes around False in my_expectation

24    def test_passing_booleans(self):
25        reality = src.telephone.text(False)
26        my_expectation = 'I got: False'
27        self.assertEqual(reality, my_expectation)
28
29
30# Exceptions seen

the test passes.


REFACTOR: make it better


  • I add an assertion for True

    24    def test_passing_booleans(self):
    25        reality = src.telephone.text(False)
    26        my_expectation = 'I got: False'
    27        self.assertEqual(reality, my_expectation)
    28
    29        reality = src.telephone.text(True)
    30        my_expectation = 'I got: "True"'
    31        self.assertEqual(reality, my_expectation)
    32
    33
    34# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'I got: True' != 'I got: "True"'
    

I remove the quotes around True in my_expectation

29        reality = src.telephone.text(True)
30        my_expectation = 'I got: True'
31        self.assertEqual(reality, my_expectation)
32
33
34# Exceptions seen

the test passes.

  • I add a variable to use to remove repetition of False

    24    def test_passing_booleans(self):
    25        false = False
    26        reality = src.telephone.text(False)
    27        my_expectation = 'I got: False'
    28        self.assertEqual(reality, my_expectation)
    
  • I use the variable with a string interpolation to remove repetition of False

    24    def test_passing_booleans(self):
    25        false = False
    26        # reality = src.telephone.text(False)
    27        reality = src.telephone.text(false)
    28        # my_expectation = 'I got: False'
    29        my_expectation = f'I got: {false}'
    30        self.assertEqual(reality, my_expectation)
    

    the test is still green.

  • I add a variable to use to remove repetition of True

    24    def test_passing_booleans(self):
    25        false = False
    26        # reality = src.telephone.text(False)
    27        reality = src.telephone.text(false)
    28        # my_expectation = 'I got: False'
    29        my_expectation = f'I got: {false}'
    30        self.assertEqual(reality, my_expectation)
    31
    32        true = True
    33        reality = src.telephone.text(True)
    34        my_expectation = 'I got: True'
    35        self.assertEqual(reality, my_expectation)
    
  • I use the variable with an f-string to remove repetition of True

    32        true = True
    33        # reality = src.telephone.text(True)
    34        reality = src.telephone.text(true)
    35        # my_expectation = 'I got: True'
    36        my_expectation = f'I got: {true}'
    37        self.assertEqual(reality, my_expectation)
    38
    39
    40# Exceptions seen
    

    still green.

  • I remove the comments

    24    def test_passing_booleans(self):
    25        false = False
    26        reality = src.telephone.text(false)
    27        my_expectation = f'I got: {false}'
    28        self.assertEqual(reality, my_expectation)
    29
    30        true = True
    31        reality = src.telephone.text(true)
    32        my_expectation = f'I got: {true}'
    33        self.assertEqual(reality, my_expectation)
    34
    35
    36# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am 'add test_passing_booleans'
    

    the terminal shows a summary of the changes then goes back to the command line.

I can pass booleans from a test to a function.


test_passing_an_integer

I can pass an integer (a whole number with no decimals) from a test to a function


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test for an integer

    30        true = True
    31        reality = src.telephone.text(true)
    32        my_expectation = f'I got: {true}'
    33        self.assertEqual(reality, my_expectation)
    34
    35    def test_passing_an_integer(self):
    36        reality = src.telephone.text(1234)
    37        my_expectation = 'I got: "1234"'
    38        self.assertEqual(reality, my_expectation)
    39
    40
    41# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'I got: 1234' != "I got: '1234'"
    

GREEN: make it pass


I remove the quotes around the integer in my_expectation

35    def test_passing_an_integer(self):
36        reality = src.telephone.text(1234)
37        my_expectation = 'I got: 1234'
38        self.assertEqual(reality, my_expectation)
39
40
41# Exceptions seen

the test passes.


REFACTOR: make it better


  • I add a variable to use to remove repetition of 1234

    35    def test_passing_an_integer(self):
    36        an_integer = 1234
    37        reality = src.telephone.text(1234)
    38        my_expectation = 'I got: 1234'
    39        self.assertEqual(reality, my_expectation)
    40
    41
    42# Exceptions seen
    
  • I use the variable with string interpolation to remove repetition of 1234

    35    def test_passing_an_integer(self):
    36        an_integer = 1234
    37        # reality = src.telephone.text(1234)
    38        reality = src.telephone.text(an_integer)
    39        # my_expectation = 'I got: 1234'
    40        my_expectation = f'I got: {an_integer}'
    41        self.assertEqual(reality, my_expectation)
    42
    43
    44# Exceptions seen
    

    the test is still green.

  • I remove the comments

    35    def test_passing_an_integer(self):
    36        an_integer = 1234
    37        reality = src.telephone.text(an_integer)
    38        my_expectation = f'I got: {an_integer}'
    39        self.assertEqual(reality, my_expectation)
    40
    41
    42# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit --all --message \
    'add test_passing_an_integer'
    

    the terminal shows a summary of the changes then goes back to the command line.

I can pass an integer from a test to a function.


test_passing_a_float

I can pass a float (binary floating point decimal number) from a test to a function


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test for a float (binary floating point decimal numbers)

    35    def test_passing_an_integer(self):
    36        an_integer = 1234
    37        reality = src.telephone.text(an_integer)
    38        my_expectation = f'I got: {an_integer}'
    39        self.assertEqual(reality, my_expectation)
    40
    41    def test_passing_a_float(self):
    42        reality = src.telephone.text(1.234)
    43        my_expectation = 'I got: "1.234"'
    44        self.assertEqual(reality, my_expectation)
    45
    46
    47# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'I got: 1.234' != 'I got: "1.234"'
    

GREEN: make it pass


I remove the quotes around the float in my_expectation

41    def test_passing_a_float(self):
42        reality = src.telephone.text(1.234)
43        my_expectation = 'I got: 1.234'
44        self.assertEqual(reality, my_expectation)
45
46
47# Exceptions seen

the test passes.


REFACTOR: make it better


  • I add a variable to use to remove repetition of 1.234

    41    def test_passing_a_float(self):
    42        a_float = 1.234
    43        reality = src.telephone.text(1.234)
    44        my_expectation = 'I got: "1.234"'
    45        self.assertEqual(reality, my_expectation)
    46
    47
    48# Exceptions seen
    
  • I use the variable with an f-string to remove repetition of 1.234

    41    def test_passing_a_float(self):
    42        a_float = 1.234
    43        # reality = src.telephone.text(1.234)
    44        reality = src.telephone.text(a_float)
    45        # my_expectation = 'I got: "1.234"'
    46        my_expectation = f'I got: {a_float}'
    47        self.assertEqual(reality, my_expectation)
    48
    49
    50# Exceptions seen
    

    the test is still green.

  • I remove the comments

    41    def test_passing_a_float(self):
    42        a_float = 1.234
    43        reality = src.telephone.text(a_float)
    44        my_expectation = f'I got: {a_float}'
    45        self.assertEqual(reality, my_expectation)
    46
    47
    48# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit --all --message 'add test_passing_a_float'
    

    the terminal shows a summary of the changes then goes back to the command line.

I can pass a float from a test to a function.


test_passing_a_tuple

I can pass a tuple (anything in parentheses ( ) separated by a comma) from a test to a function


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test for a tuple

    41    def test_passing_a_float(self):
    42        a_float = 1.234
    43        reality = src.telephone.text(a_float)
    44        my_expectation = f'I got: {a_float}'
    45        self.assertEqual(reality, my_expectation)
    46
    47    def test_passing_a_tuple(self):
    48        reality = src.telephone.text((1, 2, 3, 'n'))
    49        my_expectation = 'I got: (1, 2, 3, n)'
    50        self.assertEqual(reality, my_expectation)
    51
    52
    53# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: "I got: (1, 2, 3, 'n')"
                 != 'I got: "(1, 2, 3, n)"'
    

GREEN: make it pass


I change the tuple in my_expectation to match reality

47    def test_passing_a_tuple(self):
48        reality = src.telephone.text((1, 2, 3, 'n'))
49        my_expectation = "I got: (1, 2, 3, 'n')"
50        self.assertEqual(reality, my_expectation)
51
52
53# Exceptions seen

the test passes.


REFACTOR: make it better


  • I add a variable to use to remove repetition of the tuple

    47    def test_passing_a_tuple(self):
    48        a_tuple = (1, 2, 3, 'n')
    49        reality = src.telephone.text((1, 2, 3, 'n'))
    50        my_expectation = "I got: (1, 2, 3, 'n')"
    51        self.assertEqual(reality, my_expectation)
    52
    53
    54# Exceptions seen
    
  • I use the variable with string interpolation to remove repetition of the tuple

    47    def test_passing_a_tuple(self):
    48        a_tuple = (1, 2, 3, 'n')
    49        # reality = src.telephone.text((1, 2, 3, 'n'))
    50        reality = src.telephone.text(a_tuple)
    51        # my_expectation = "I got: (1, 2, 3, 'n')"
    52        my_expectation = f"I got: {a_tuple}"
    53        self.assertEqual(reality, my_expectation)
    54
    55
    56# Exceptions seen
    

    the test is still green.

  • I remove the comments

    47    def test_passing_a_tuple(self):
    48        a_tuple = (1, 2, 3, 'n')
    49        reality = src.telephone.text(a_tuple)
    50        my_expectation = f"I got: {a_tuple}"
    51        self.assertEqual(reality, my_expectation)
    52
    53
    54# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am 'add test_passing_a_tuple'
    

    the terminal shows a summary of the changes then goes back to the command line.

I can pass a tuple from a test to a function.


test_passing_a_list

I can pass a list (anything in square brackets [ ]) from a test to a function


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test for a list

    47    def test_passing_a_tuple(self):
    48        a_tuple = (1, 2, 3, 'n')
    49        reality = src.telephone.text(a_tuple)
    50        my_expectation = f"I got: {a_tuple}"
    51        self.assertEqual(reality, my_expectation)
    52
    53    def test_passing_a_list(self):
    54        reality = src.telephone.text([1, 2, 3, 'n'])
    55        my_expectation = 'I got: [1, 2, 3, "n"]'
    56        self.assertEqual(reality, my_expectation)
    57
    58
    59# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: "I got: [1, 2, 3, 'n']"
                 != "I got: '[1, 2, 3, n]'"
    

GREEN: make it pass


I change the list in my_expectation to match reality

53    def test_passing_a_list(self):
54        reality = src.telephone.text([1, 2, 3, "n"])
55        my_expectation = "I got: [1, 2, 3, 'n']"
56        self.assertEqual(reality, my_expectation)
57
58
59# Exceptions seen

the test passes. Python changed the double quotes (") in the list to a single quote (').


REFACTOR: make it better


  • I add a variable to use to remove repetition of the list

    53    def test_passing_a_list(self):
    54        a_list = [1, 2, 3, 'n']
    55        reality = src.telephone.text([1, 2, 3, 'n'])
    56        my_expectation = "I got: [1, 2, 3, 'n']"
    57        self.assertEqual(reality, my_expectation)
    58
    59
    60# Exceptions seen
    
  • I use the variable with an f-string to remove repetition of the list

    53    def test_passing_a_list(self):
    54        a_list = [1, 2, 3, 'n']
    55        # reality = src.telephone.text([1, 2, 3, 'n'])
    56        reality = src.telephone.text(a_list)
    57        # my_expectation = "I got: [1, 2, 3, 'n']"
    58        my_expectation = f"I got: {a_list}"
    59        self.assertEqual(reality, my_expectation)
    60
    61
    62# Exceptions seen
    

    the test is still green.

  • I remove the comments

    53    def test_passing_a_list(self):
    54        a_list = [1, 2, 3, 'n']
    55        reality = src.telephone.text(a_list)
    56        my_expectation = f"I got: {a_list}"
    57        self.assertEqual(reality, my_expectation)
    58
    59
    60# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit --all --message 'add test_passing_a_list'
    

    the terminal shows a summary of the changes then goes back to the command line.

I can pass a list from a test to a function.


test_passing_a_dictionary

I can pass a dictionary (anything in curly braces { } separated by a comma) from a test to a function


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test for a dictionary

    53    def test_passing_a_list(self):
    54        a_list = [1, 2, 3, 'n']
    55        reality = src.telephone.text(a_list)
    56        my_expectation = f"I got: {a_list}"
    57        self.assertEqual(reality, my_expectation)
    58
    59    def test_passing_a_dictionary(self):
    60        reality = src.telephone.text(
    61            {
    62                'key1': 'value1',
    63                'keyN': [0, 1, 2, 'n'],
    64            }
    65        )
    66        my_expectation = (
    67            "I got: "
    68            "{key1: value1, keyN: [0, 1, 2, n]}"
    69        )
    70        self.assertEqual(reality, my_expectation)
    71
    72
    73# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        "I got: {'key1': 'value1', 'keyN': [0, 1, 2, 'n']}"
     != 'I got: { key1:   value1 ,  keyN : [0, 1, 2,  n ]}'
    

GREEN: make it pass


I change my_expectation to match reality

59    def test_passing_a_dictionary(self):
60        reality = src.telephone.text(
61            {
62                'key1': 'value1',
63                'keyN': [0, 1, 2, 'n'],
64            }
65        )
66        my_expectation = (
67            "I got: "
68            "{'key1': 'value1', "
69            "'keyN': [0, 1, 2, 'n']}"
70        )
71        self.assertEqual(reality, my_expectation)
72
73
74# Exceptions seen

the test passes.


REFACTOR: make it better


  • I add a variable to use to remove repetition of the dictionary

    59    def test_passing_a_dictionary(self):
    60        a_dictionary = {
    61            'key1': 'value1',
    62            'keyN': [0, 1, 2, 'n'],
    63        }
    64        reality = src.telephone.text(
    65            {
    66                'key1': 'value1',
    67                'keyN': [0, 1, 2, 'n'],
    68            }
    69        )
    70        my_expectation = (
    71            "I got: "
    72            "{'key1': 'value1', 'keyN': [0, 1, 2, 'n']}"
    73        )
    74        self.assertEqual(reality, my_expectation)
    75
    76
    77# Exceptions seen
    
  • I use the variable with string interpolation to remove repetition of the dictionary

    59    def test_passing_a_dictionary(self):
    60        a_dictionary = {
    61            'key1': 'value1',
    62            'keyN': [0, 1, 2, 'n'],
    63        }
    64        # reality = src.telephone.text(
    65        #     {
    66        #         'key1': 'value1',
    67        #         'keyN': [0, 1, 2, 'n'],
    68        #     }
    69        # )
    70        reality = src.telephone.text(a_dictionary)
    71        # my_expectation = (
    72        #     "I got: "
    73        #     "{'key1': 'value1', 'keyN': [0, 1, 2, 'n']}"
    74        # )
    75        my_expectation = f'I got: {a_dictionary}'
    76        self.assertEqual(reality, my_expectation)
    77
    78
    79# Exceptions seen
    

    the test is still green.

  • I remove the comments

    59    def test_passing_a_dictionary(self):
    60        a_dictionary = {
    61            'key1': 'value1',
    62            'keyN': [0, 1, 2, 'n'],
    63        }
    64        reality = src.telephone.text(a_dictionary)
    65        my_expectation = f'I got: {a_dictionary}'
    66        self.assertEqual(reality, my_expectation)
    67
    68
    69# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am 'add test_passing_a_dictionary'
    

    the terminal shows a summary of the changes then goes back to the command line.

I can pass a dictionary from a test to a function.


test_passing_a_class

I can pass an object from a test to a function


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a failing test to see what happens when I pass a class from a test to the text function, in test_telephone.py

    59    def test_passing_a_dictionary(self):
    60        a_dictionary = {
    61            'key1': 'value1',
    62            'keyN': [0, 1, 2, 'n'],
    63        }
    64        reality = src.telephone.text(a_dictionary)
    65        my_expectation = f'I got: {a_dictionary}'
    66        self.assertEqual(reality, my_expectation)
    67
    68    def test_passing_a_class(self):
    69        reality = src.telephone.text(object)
    70        my_expectation = 'I got: object'
    71        self.assertEqual(reality, my_expectation)
    72
    73
    74# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: "I got: <class 'object'>"
                 != 'I got:         object'
    

    object is the mother class that all Python classes come from, and everything in Python is an object


GREEN: make it pass


I change my_expectation to match reality

68    def test_passing_a_class(self):
69        reality = src.telephone.text(object)
70        my_expectation = "I got: <class 'object'>"
71        self.assertEqual(reality, my_expectation)
72
73
74# Exceptions seen

the test passes.


REFACTOR: make it better


  • I add another assertion with the TestTelephone class to test_passing_a_class in test_telephone.py

    68    def test_passing_a_class(self):
    69        reality = src.telephone.text(object)
    70        my_expectation = "I got: <class 'object'>"
    71        self.assertEqual(reality, my_expectation)
    72
    73        reality = src.telephone.text(TestTelephone)
    74        my_expectation = "I got: <class 'object'>"
    75        self.assertEqual(reality, my_expectation)
    76
    77
    78# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        "I got: <class 'tests.test_telephone.TestTelephone'>"
     != "I got: <class 'object'>"
    
  • I change my_expectation to match reality

    73        reality = src.telephone.text(TestTelephone)
    74        my_expectation = (
    75            "I got: <class "
    76            "'tests.test_telephone.TestTelephone'>"
    77        )
    78        self.assertEqual(reality, my_expectation)
    79
    80
    81# Exceptions seen
    

    the test passes. What does tests.test_telephone.TestTelephone point to?

    • tests is the folder

    • test_telephone is test_telephone.py in the tests folder

    • TestTelephone is the class (object) that is defined on line 5 of test_telephone.py in the tests folder

  • I add another assertion

    73        reality = src.telephone.text(TestTelephone)
    74        my_expectation = (
    75            "I got: <class "
    76            "'tests.test_telephone.TestTelephone'>"
    77        )
    78        self.assertEqual(reality, my_expectation)
    79
    80        reality = src.telephone.text(self)
    81        my_expectation = "I got: self"
    82        self.assertEqual(reality, my_expectation)
    83
    84
    85# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        'I got: test_passing_a_class (
            tests.test_telephone
                 .TestTelephone.test_passing_a_class
        )'
        != 'I got: self'
    
  • I change my_expectation to match reality

    80        reality = src.telephone.text(self)
    81        my_expectation = (
    82            "I got: test_passing_a_class "
    83            "(tests.test_telephone.TestTelephone"
    84            ".test_passing_a_class)"
    85        )
    86        self.assertEqual(reality, my_expectation)
    87
    88
    89# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit --all --message 'add test_passing_a_class'
    

    the terminal shows a summary of the changes then goes back to the command line.

I can pass an object from a test to a function.


test_telephone

Time to write the program that makes the tests pass without looking at test_telephone.py


RED: make it fail


  • I close test_telephone.py in the editor

  • I delete the text in telephone.py and the terminal shows 9 failures. I start with the last AttributeError

    FAILED ...test_passing_a_class - AttributeError:
            module 'src.telephone' has no attribute 'text'
    FAILED ...test_passing_a_dictionary - AttributeError:
            module 'src.telephone' has no attribute 'text'
    FAILED ...test_passing_a_float - AttributeError:
            module 'src.telephone' has no attribute 'text'
    FAILED ...test_passing_a_list - AttributeError:
            module 'src.telephone' has no attribute 'text'
    FAILED ...test_passing_a_string - AttributeError:
            module 'src.telephone' has no attribute 'text'
    FAILED ...test_passing_a_tuple - AttributeError:
            module 'src.telephone' has no attribute 'text'
    FAILED ...test_passing_an_integer - AttributeError:
            module 'src.telephone' has no attribute 'text'
    FAILED ...test_passing_booleans - AttributeError:
            module 'src.telephone' has no attribute 'text'
    FAILED ...test_passing_none - AttributeError:
            module 'src.telephone' has no attribute 'text'
    

GREEN: make it pass


  • I add the name to telephone.py

    1text
    

    the terminal is my friend, and shows NameError

    NameError: name 'text' is not defined
    
  • I point it to None to define it

    1text = None
    

    the terminal is my friend, and shows TypeError

    TypeError: 'NoneType' object is not callable
    
  • I make text a function to make it callable

    1def text():
    2    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: text() takes 0 positional arguments
               but 1 was given
    
  • I make the function take input

    1def text(value):
    2    return None
    

    the terminal is my friend, and shows AssertionError

    AssertionError: None != 'I got: None'
    
  • I copy the string from the terminal and paste it in the return statement to match the expectation of the test

    1def text(value):
    2    return 'I got: None'
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'I got: None' != 'I got: False'
    
  • I add a return statement to see the difference between the input and the expected output (remember the identity function?)

    1def text(value):
    2    return value
    3    return 'I got: None'
    

    the test summary info shows that every test has AssertionError

    AssertionError:
        <class 'object'> != "I got: <class 'object'>"
    AssertionError:
                 {'key1': 'value1', 'keyN': [0, 1, 2, 'n']}
      != "I got: {'key1': 'value1', 'keyN': [0, 1, 2, 'n']}"
    AssertionError:
        1.234 != 'I got: 1.234'
    AssertionError:
        [1, 2, 3, 'n'] != "I got: [1, 2, 3, 'n']"
    AssertionError:
        'hello' != 'I got: hello'
    AssertionError:
        (1, 2, 3, 'n') != "I got: (1, 2, 3, 'n')"
    AssertionError:
        1234 != 'I got: 1234'
    AssertionError:
        False != 'I got: False'
    AssertionError:
        None != 'I got: None'
    

    they all expect the input (value) as part of the message

  • I remove the first return statement then make the second one use an f-string

    1def text(value):
    2    return f'I got: {value}'
    

    and all the tests are passing! I am a programmer!!


close the project

  • I close telephone.py in the editor

  • I click in the terminal where the tests are running, then use q on the keyboard to leave the tests. The terminal goes back to the command line.

  • I change directory to the parent of telephone

    cd ..
    

    the terminal shows

    .../pumping_python
    

    I am back in the pumping_python directory


review

Here are the tests I ran to see what happens when I pass Python basic data structures from a test to a program and place them in an f-string which is one way to do string interpolation

I also saw these Exceptions


code from the chapter

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


what is next?

You now know:

would you like to test using dictionaries and functions to make a person?


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.