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 show how I can pass input data from a test to a function in a module.


preview

I have these tests by the end of the chapter


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 'setup project'
    

    the terminal_ shows

    [main (root-commit) a0b12c3] setup 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

    1# text
    2text = None
    

    the terminal_ is my friend, and shows TypeError

    TypeError: 'NoneType' object is not callable
    

    because I cannot call None like 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_

    1# text
    2# text = None
    3def text():
    4    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

    1# text
    2# text = None
    3# def text():
    4def text(the_input):
    5    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

    1# text
    2# text = None
    3# def text():
    4def text(the_input):
    5    # return None
    6    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

    1# text
    2# text = None
    3# def text():
    4def text(the_input):
    5    # return None
    6    # return 'I got: hello'
    7    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_

    1# text
    2# text = None
    3# def text():
    4def text(the_input):
    5    # return None
    6    # return 'I got: hello'
    7    # return 'I got: yes'
    8    return f'I got: {the_input}'
    

    the test passes.

  • I remove the commented lines

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

    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 to 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        my_expectation = 'I got: None'
22        self.assertEqual(reality, my_expectation)
23
24
25# Exceptions seen

the test passes.


REFACTOR: make it better


  • I remove the comments

    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
    
  • 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        reality = src.telephone.text(None)
    20        my_expectation = 'I got: None'
    21        self.assertEqual(reality, my_expectation)
    22
    23    def test_passing_booleans(self):
    24        reality = src.telephone.text(False)
    25        my_expectation = 'I got: "False"'
    26        self.assertEqual(reality, my_expectation)
    27
    28
    29# 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

23    def test_passing_booleans(self):
24        reality = src.telephone.text(False)
25        # my_expectation = 'I got: "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

    23    def test_passing_booleans(self):
    24        reality = src.telephone.text(False)
    25        my_expectation = 'I got: False'
    26        self.assertEqual(reality, my_expectation)
    27
    28        reality = src.telephone.text(True)
    29        my_expectation = 'I got: "True"'
    30        self.assertEqual(reality, my_expectation)
    31
    32
    33# 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        my_expectation = 'I got: True'
    32        self.assertEqual(reality, my_expectation)
    33
    34
    35# Exceptions seen
    

    the test passes.

  • I remove the comments

    23    def test_passing_booleans(self):
    24        reality = src.telephone.text(False)
    25        my_expectation = 'I got: False'
    26        self.assertEqual(reality, my_expectation)
    27
    28        reality = src.telephone.text(True)
    29        my_expectation = 'I got: True'
    30        self.assertEqual(reality, my_expectation)
    31
    32
    33# 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 without 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_

    28        reality = src.telephone.text(True)
    29        my_expectation = 'I got: True'
    30        self.assertEqual(reality, my_expectation)
    31
    32    def test_passing_an_integer(self):
    33        reality = src.telephone.text(1234)
    34        my_expectation = 'I got: "1234"'
    35        self.assertEqual(reality, my_expectation)
    36
    37
    38# 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

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

the test passes.


REFACTOR: make it better


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

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

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

    the test is still green.

  • I remove the comments

    32    def test_passing_an_integer(self):
    33        an_integer = 1234
    34        reality = src.telephone.text(an_integer)
    35        my_expectation = f'I got: {an_integer}'
    36        self.assertEqual(reality, my_expectation)
    37
    38
    39# 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)

    32    def test_passing_an_integer(self):
    33        an_integer = 1234
    34        reality = src.telephone.text(an_integer)
    35        my_expectation = f'I got: {an_integer}'
    36        self.assertEqual(reality, my_expectation)
    37
    38    def test_passing_a_float(self):
    39        reality = src.telephone.text(1.234)
    40        my_expectation = 'I got: "1.234"'
    41        self.assertEqual(reality, my_expectation)
    42
    43
    44# 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

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

the test passes.


REFACTOR: make it better


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

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

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

    the test is still green.

  • I remove the comments

    38    def test_passing_a_float(self):
    39        a_float = 1.234
    40        reality = src.telephone.text(a_float)
    41        my_expectation = f'I got: {a_float}'
    42        self.assertEqual(reality, my_expectation)
    43
    44
    45# Exceptions seen
    
  • I add a git_ commit message in the other terminal_

    git commit -am '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_

    38    def test_passing_a_float(self):
    39        a_float = 1.234
    40        reality = src.telephone.text(a_float)
    41        my_expectation = f'I got: {a_float}'
    42        self.assertEqual(reality, my_expectation)
    43
    44    def test_passing_a_tuple(self):
    45        reality = src.telephone.text((1, 2, 3, 'n'))
    46        my_expectation = 'I got: (1, 2, 3, n)'
    47        self.assertEqual(reality, my_expectation)
    48
    49
    50# 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

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

the test passes.


REFACTOR: make it better


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

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

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

    the test is still green.

  • I remove the comments

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

    git commit --all --message '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

    44    def test_passing_a_tuple(self):
    45        a_tuple = (1, 2, 3, 'n')
    46        reality = src.telephone.text(a_tuple)
    47        my_expectation = f"I got: {a_tuple}"
    48        self.assertEqual(reality, my_expectation)
    49
    50    def test_passing_a_list(self):
    51        reality = src.telephone.text([1, 2, 3, 'n'])
    52        my_expectation = 'I got: [1, 2, 3, "n"]'
    53        self.assertEqual(reality, my_expectation)
    54
    55
    56# 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

50    def test_passing_a_list(self):
51        reality = src.telephone.text([1, 2, 3, 'n'])
52        # my_expectation = 'I got: [1, 2, 3, "n"]'
53        my_expectation = "I got: [1, 2, 3, 'n']"
54        self.assertEqual(reality, my_expectation)
55
56
57# 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

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

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

    the test is still green.

  • I remove the comments

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

    git commit -am '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

    50    def test_passing_a_list(self):
    51        a_list = [1, 2, 3, 'n']
    52        reality = src.telephone.text(a_list)
    53        my_expectation = f"I got: {a_list}"
    54        self.assertEqual(reality, my_expectation)
    55
    56    def test_passing_a_dictionary(self):
    57        reality = src.telephone.text(
    58            {
    59                'key1': 'value1',
    60                'keyN': [0, 1, 2, 'n'],
    61            }
    62        )
    63        my_expectation = (
    64            "I got: "
    65            "{key1: value1, keyN: [0, 1, 2, n]}"
    66        )
    67        self.assertEqual(reality, my_expectation)
    68
    69
    70# 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

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

the test passes.


REFACTOR: make it better


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

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

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

    the test is still green.

  • I remove the comments

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

    git commit --all --message '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

    56    def test_passing_a_dictionary(self):
    57        a_dictionary = {
    58            'key1': 'value1',
    59            'keyN': [0, 1, 2, 'n'],
    60        }
    61        reality = src.telephone.text(a_dictionary)
    62        my_expectation = f'I got: {a_dictionary}'
    63        self.assertEqual(reality, my_expectation)
    64
    65    def test_passing_a_class(self):
    66        reality = src.telephone.text(object)
    67        my_expectation = 'I got: object'
    68        self.assertEqual(reality, my_expectation)
    69
    70
    71# 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

65    def test_passing_a_class(self):
66        reality = src.telephone.text(object)
67        # my_expectation = 'I got: object'
68        my_expectation = "I got: <class 'object'>"
69        self.assertEqual(reality, my_expectation)
70
71
72# 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

    65    def test_passing_a_class(self):
    66        reality = src.telephone.text(object)
    67        # my_expectation = 'I got: object'
    68        my_expectation = "I got: <class 'object'>"
    69        self.assertEqual(reality, my_expectation)
    70
    71        reality = src.telephone.text(TestTelephone)
    72        my_expectation = "I got: <class 'object'>"
    73        self.assertEqual(reality, my_expectation)
    74
    75
    76# 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

    71        reality = src.telephone.text(TestTelephone)
    72        # my_expectation = "I got: <class 'object'>"
    73        my_expectation = (
    74            "I got: <class"
    75            f" 'tests.test_telephone.TestTelephone'>"
    76        )
    77        self.assertEqual(reality, my_expectation)
    78
    79
    80# 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

    71        reality = src.telephone.text(TestTelephone)
    72        # my_expectation = "I got: <class 'object'>"
    73        my_expectation = (
    74            "I got: <class"
    75            f" 'tests.test_telephone.TestTelephone'>"
    76        )
    77        self.assertEqual(reality, my_expectation)
    78
    79        reality = src.telephone.text(self)
    80        my_expectation = "I got: self"
    81        self.assertEqual(reality, my_expectation)
    82
    83
    84# 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

    79        reality = src.telephone.text(self)
    80        # my_expectation = "I got: 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 an assertion for bool (the class for booleans)

    79        reality = src.telephone.text(self)
    80        # my_expectation = "I got: 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        reality = src.telephone.text(bool)
    89        my_expectation = 'I got: bool'
    90        self.assertEqual(reality, my_expectation)
    91
    92
    93# Exceptions seen
    

    the terminal_ is my friend, and shows AssertionError

    AssertionError: "I got: <class 'bool'>" != 'I got: bool'
    
  • I change my_expectation to match reality

    88        reality = src.telephone.text(bool)
    89        # my_expectation = 'I got: bool'
    90        my_expectation = "I got: <class 'bool'>"
    91        self.assertEqual(reality, my_expectation)
    92
    93
    94# Exceptions seen
    

    the test passes.

  • I add an assertion for int_ (the class for whole numbers without decimals)

    88        reality = src.telephone.text(bool)
    89        # my_expectation = 'I got: bool'
    90        my_expectation = "I got: <class 'bool'>"
    91        self.assertEqual(reality, my_expectation)
    92
    93        reality = src.telephone.text(int)
    94        my_expectation = "I got: int"
    95        self.assertEqual(reality, my_expectation)
    96
    97
    98# Exceptions seen
    

    the terminal_ is my friend, and shows AssertionError

    AssertionError: "I got: <class 'int'>" != 'I got: int'
    
  • I change my_expectation to match reality

    93        reality = src.telephone.text(int)
    94        # my_expectation = "I got: int"
    95        my_expectation = "I got: <class 'int'>"
    96        self.assertEqual(reality, my_expectation)
    97
    98
    99# Exceptions seen
    

    the test passes

  • I add an assertion for float_ (the class for binary floating point decimal numbers)

     93        reality = src.telephone.text(int)
     94        # my_expectation = 'I got: int'
     95        my_expectation = "I got: <class 'int'>"
     96        self.assertEqual(reality, my_expectation)
     97
     98        reality = src.telephone.text(float)
     99        my_expectation = "I got: float"
    100        self.assertEqual(reality, my_expectation)
    101
    102
    103# Exceptions seen
    

    the terminal_ is my friend, and shows AssertionError

    AssertionError: "I got: <class 'float'>" != 'I got: float'
    
  • I change my_expectation to match reality

     98        reality = src.telephone.text(float)
     99        # my_expectation = "I got: float"
    100        my_expectation = "I got: <class 'float'>"
    101        self.assertEqual(reality, my_expectation)
    102
    103
    104# Exceptions seen
    

    the test passes

  • I add an assertion for str_ (the class for anything in quotes)

     98        reality = src.telephone.text(float)
     99        # my_expectation = "I got: float"
    100        my_expectation = "I got: <class 'float'>"
    101        self.assertEqual(reality, my_expectation)
    102
    103        reality = src.telephone.text(str)
    104        my_expectation = "I got: str"
    105        self.assertEqual(reality, my_expectation)
    106
    107
    108# Exceptions seen
    

    the terminal_ is my friend, and shows AssertionError

    AssertionError: "I got: <class 'str'>" != 'I got: str'
    
  • I change my_expectation to match reality

    103        reality = src.telephone.text(str)
    104        # my_expectation = "I got: str"
    105        my_expectation = "I got: <class 'str'>"
    106        self.assertEqual(reality, my_expectation)
    107
    108
    109# Exceptions seen
    

    the test passes

  • I add an assertion for tuple_ (the class for anything in parentheses ( ) separated by a comma)

    103        reality = src.telephone.text(str)
    104        # my_expectation = "I got: str"
    105        my_expectation = "I got: <class 'str'>"
    106        self.assertEqual(reality, my_expectation)
    107
    108        reality = src.telephone.text(tuple)
    109        my_expectation = "I got: tuple"
    110        self.assertEqual(reality, my_expectation)
    111
    112
    113# Exceptions seen
    

    the terminal_ is my friend, and shows AssertionError

    AssertionError: "I got: <class 'tuple'>" != 'I got: tuple'
    
  • I change my_expectation to match reality

    108        reality = src.telephone.text(tuple)
    109        # my_expectation = "I got: tuple"
    110        my_expectation = "I got: <class 'tuple'>"
    111        self.assertEqual(reality, my_expectation)
    112
    113
    114# Exceptions seen
    
  • I add an assertion for list (the class for anything in square brackets ‘[ ]’)

    108        reality = src.telephone.text(tuple)
    109        # my_expectation = "I got: tuple"
    110        my_expectation = "I got: <class 'tuple'>"
    111        self.assertEqual(reality, my_expectation)
    112
    113        reality = src.telephone.text(list)
    114        my_expectation = "I got: list"
    115        self.assertEqual(reality, my_expectation)
    116
    117
    118# Exceptions seen
    

    the terminal_ is my friend, and shows AssertionError

    AssertionError: "I got: <class 'list'>" != 'I got: list'
    
  • I change my_expectation to match reality

    113        reality = src.telephone.text(list)
    114        # my_expectation = "I got: list"
    115        my_expectation = "I got: <class 'list'>"
    116        self.assertEqual(reality, my_expectation)
    117
    118
    119# Exceptions seen
    

    the test passes

  • I add an assertion for set_ (the class anything in curly braces { } separated by a comma)

    113        reality = src.telephone.text(list)
    114        # my_expectation = "I got: list"
    115        my_expectation = "I got: <class 'list'>"
    116        self.assertEqual(reality, my_expectation)
    117
    118        reality = src.telephone.text(set)
    119        my_expectation = "I got: set"
    120        self.assertEqual(reality, my_expectation)
    121
    122
    123# Exceptions seen
    

    the terminal_ is my friend, and shows AssertionError

    AssertionError: "I got: <class 'set'>" != 'I got: set'
    
  • I change my_expectation to match reality

    118        reality = src.telephone.text(set)
    119        # my_expectation = "I got: set"
    120        my_expectation = "I got: <class 'set'>"
    121        self.assertEqual(reality, my_expectation)
    122
    123
    124# Exceptions seen
    

    the test passes

  • I add an assertion for dict (the class for any key-value pairs in curly braces ‘{ }’ separated by a comma)

    118        reality = src.telephone.text(set)
    119        # my_expectation = "I got: set"
    120        my_expectation = "I got: <class 'set'>"
    121        self.assertEqual(reality, my_expectation)
    122
    123        reality = src.telephone.text(dict)
    124        my_expectation = "I got: dict"
    125        self.assertEqual(reality, my_expectation)
    126
    127
    128# Exceptions seen
    

    the terminal_ is my friend, and shows AssertionError

    AssertionError: "I got: <class 'dict'>" != 'I got: dict'
    
  • I change my_expectation to match reality

    123        reality = src.telephone.text(dict)
    124        # my_expectation = "I got: dict"
    125        my_expectation = "I got: <class 'dict'>"
    126        self.assertEqual(reality, my_expectation)
    127
    128
    129# Exceptions seen
    

    the test passes

  • I remove the commented lines

     65    def test_passing_a_class(self):
     66        reality = src.telephone.text(object)
     67        my_expectation = "I got: <class 'object'>"
     68        self.assertEqual(reality, my_expectation)
     69
     70        reality = src.telephone.text(TestTelephone)
     71        my_expectation = (
     72            "I got: <class"
     73            f" 'tests.test_telephone.TestTelephone'>"
     74        )
     75        self.assertEqual(reality, my_expectation)
     76
     77        reality = src.telephone.text(self)
     78        my_expectation = (
     79            "I got: test_passing_a_class"
     80            " (tests.test_telephone.TestTelephone"
     81            ".test_passing_a_class)"
     82        )
     83        self.assertEqual(reality, my_expectation)
     84
     85        reality = src.telephone.text(bool)
     86        my_expectation = "I got: <class 'bool'>"
     87        self.assertEqual(reality, my_expectation)
     88
     89        reality = src.telephone.text(int)
     90        my_expectation = "I got: <class 'int'>"
     91        self.assertEqual(reality, my_expectation)
     92
     93        reality = src.telephone.text(float)
     94        my_expectation = "I got: <class 'float'>"
     95        self.assertEqual(reality, my_expectation)
     96
     97        reality = src.telephone.text(str)
     98        my_expectation = "I got: <class 'str'>"
     99        self.assertEqual(reality, my_expectation)
    100
    101        reality = src.telephone.text(tuple)
    102        my_expectation = "I got: <class 'tuple'>"
    103        self.assertEqual(reality, my_expectation)
    104
    105        reality = src.telephone.text(list)
    106        my_expectation = "I got: <class 'list'>"
    107        self.assertEqual(reality, my_expectation)
    108
    109        reality = src.telephone.text(set)
    110        my_expectation = "I got: <class 'set'>"
    111        self.assertEqual(reality, my_expectation)
    112
    113        reality = src.telephone.text(dict)
    114        my_expectation = "I got: <class 'dict'>"
    115        self.assertEqual(reality, my_expectation)
    116
    117
    118# Exceptions seen
    
  • I add a git_ commit message in the other terminal_

    git commit -am '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

    1# text
    2text = None
    

    the terminal_ is my friend, and shows TypeError

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

    1# text
    2# text = None
    3def text():
    4    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

    1# text
    2# text = None
    3# def text():
    4def text(value):
    5    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

    1# text
    2# text = None
    3# def text():
    4def text(value):
    5    # return None
    6    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?)

    1# text
    2# text = None
    3# def text():
    4def text(value):
    5    # return None
    6    # return 'I got: None'
    7    return value
    

    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 add a `return statement`_ with an f-string

    1# text
    2# text = None
    3# def text():
    4def text(value):
    5    # return None
    6    # return 'I got: None'
    7    # return value
    8    return f'I got: {value}'
    

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

  • I remove the commented lines

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

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.