how to make a person with f-strings


Since I can pass objects into a string with f-strings. I can write one function that makes a person instead of making one function for each person.


preview

I have these tests by the end of the chapter

person/tests/test_person.py
  1import src.person
  2
  3
  4def test_joe():
  5    first_name = 'joe'
  6    last_name = 'blow'
  7    sex = 'M'
  8    year_of_birth = 1996
  9
 10    reality = src.person.factory(
 11        first_name=first_name,
 12        last_name=last_name,
 13        sex=sex,
 14        year_of_birth=year_of_birth,
 15    )
 16    my_expectation = (
 17        f'{first_name}, {last_name},'
 18        f' {sex}, {year_of_birth}'
 19    )
 20    assert reality == my_expectation
 21
 22    reality = src.person.say_hello(
 23        first_name=first_name,
 24        last_name=last_name,
 25        year_of_birth=year_of_birth,
 26    )
 27    my_expectation = (
 28        f'Hello, my name is {first_name}'
 29        f' {last_name} and I am'
 30        f' {2026-year_of_birth}.'
 31    )
 32    assert reality == my_expectation
 33
 34
 35def test_jane():
 36    first_name = 'jane'
 37    last_name = 'doe'
 38    sex = 'F'
 39    year_of_birth = 1991
 40
 41    reality = src.person.factory(
 42        first_name=first_name,
 43        last_name=last_name,
 44        sex=sex,
 45        year_of_birth=year_of_birth,
 46    )
 47    my_expectation = (
 48        f'{first_name}, {last_name},'
 49        f' {sex}, {year_of_birth}'
 50    )
 51    assert reality == my_expectation
 52
 53    reality = src.person.say_hello(
 54        first_name=first_name,
 55        last_name=last_name,
 56        year_of_birth=year_of_birth,
 57    )
 58    my_expectation = (
 59        f'Hello, my name is {first_name}'
 60        f' {last_name} and I am'
 61        f' {2026-year_of_birth}.'
 62    )
 63    assert reality == my_expectation
 64
 65
 66def test_john():
 67    first_name = 'john'
 68    last_name = 'smith'
 69    sex = 'M'
 70    year_of_birth = 1580
 71
 72    reality = src.person.factory(
 73        first_name=first_name,
 74        last_name=last_name,
 75        sex=sex,
 76        year_of_birth=year_of_birth,
 77    )
 78    my_expectation = (
 79        f'{first_name}, {last_name},'
 80        f' {sex}, {year_of_birth}'
 81    )
 82    assert reality == my_expectation
 83
 84    reality = src.person.say_hello(
 85        first_name=first_name,
 86        last_name=last_name,
 87        year_of_birth=year_of_birth,
 88    )
 89    my_expectation = (
 90        f'Hello, my name is {first_name}'
 91        f' {last_name} and I am'
 92        f' {2026-year_of_birth}.'
 93    )
 94    assert reality == my_expectation
 95
 96
 97def test_mary():
 98    first_name = 'mary'
 99    last_name = 'public'
100    sex = 'F'
101    year_of_birth = 2000
102
103    reality = src.person.factory(
104        first_name=first_name,
105        last_name=last_name,
106        sex=sex,
107        year_of_birth=year_of_birth,
108    )
109    my_expectation = (
110        f'{first_name}, {last_name},'
111        f' {sex}, {year_of_birth}'
112    )
113    assert reality == my_expectation
114
115    reality = src.person.say_hello(
116        first_name=first_name,
117        last_name=last_name,
118        year_of_birth=year_of_birth,
119    )
120    my_expectation = (
121        f'Hello, my name is {first_name}'
122        f' {last_name} and I am'
123        f' {2026-year_of_birth}.'
124    )
125    assert reality == my_expectation
126
127
128# Exceptions seen
129# AssertionError
130# NameError
131# TypeError
132# AttributeError

open the project

  • I open a terminal

  • I change directory to the project

    cd person
    

    the terminal shows I am in the person folder

    .../pumping_python/person
    
  • I open test_person.py

  • I use pytest-watcher to run the tests automatically

    uv run pytest-watcher . --now
    

    the terminal shows the tests from before are passing.


test person factory

RED: make it fail


I add an assertion to test_joe in test_person.py

17  def test_joe():
18      assert joe() == 'joe, blow, M, 1996'
19
20      reality = factory()
21      my_expectation = 'joe, blow, M, 1996'
22      assert reality == my_expectation
23
24
25  def test_jane():

the terminal is my friend, and shows NameError

NameError: name 'factory' is not defined

because I have not defined factory in test_person.py, yet.


GREEN: make it pass


I add a function definition for it

1def factory():
2    return 'joe, blow, M, 1996'
3
4
5def joe():

the test passes.


REFACTOR: make it better


  • I add an assertion with a call to the factory function in test_jane

    21def test_joe():
    22    assert joe() == 'joe, blow, M, 1996'
    23
    24    reality = factory()
    25    my_expectation = 'joe, blow, M, 1996'
    26    assert reality == my_expectation
    27
    28
    29def test_jane():
    30    assert jane() == 'jane, doe, F, 1991'
    31
    32    reality = factory()
    33    my_expectation = 'jane, doe, F, 1991'
    34    assert reality == my_expectation
    35
    36
    37def test_john():
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert 'joe, blow, M, 1996'
                        == 'jane, doe, F, 1991'
    

    because the factory function always returns joe, blow, M, 1996 when it is called. It has to return a string based on the input it gets for me to be able to use it to make more than one person.

  • I make the function take input for the first name

    1# def factory():
    2def factory(first_name):
    3    return 'joe, blow, M, 1996'
    

    the terminal is my friend, and shows TypeError

    TypeError: factory() missing
               1 required positional argument:
               'first_name'
    

    because

  • I add 'jane' to the call to the factory function in test_jane

    30def test_jane():
    31    assert jane() == 'jane, doe, F, 1991'
    32
    33    # reality = factory()
    34    reality = factory('jane')
    35    my_expectation = 'jane, doe, F, 1991'
    36    assert reality == my_expectation
    37
    38
    39def test_john():
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert 'joe, blow, M, 1996'
                        == 'jane, doe, F, 1991'
    
  • I change the return statement of the factory function to an f-string to use first_name in the output

    1# def factory():
    2def factory(first_name):
    3    # return 'joe, blow, M, 1996'
    4    return f'{first_name}, blow, M, 1996'
    5
    6
    7def joe():
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert 'jane, blow, M, 1996'
                        == 'jane, doe, F, 1991'
    

    the first names match. Progress!

  • I add last_name to the return statement

     1# def factory():
     2def factory(first_name):
     3    # return 'joe, blow, M, 1996'
     4    # return f'{first_name}, blow, M, 1996'
     5    return (
     6        f'{first_name}, {last_name},'
     7        ' M, 1996'
     8    )
     9
    10
    11def joe():
    

    the terminal is my friend, and shows NameError

    NameError: name 'last_name' is not defined
    
  • I add the name to the parentheses to define it in the factory function

     1# def factory():
     2# def factory(first_name):
     3def factory(first_name, last_name):
     4    # return 'joe, blow, M, 1996'
     5    # return f'{first_name}, blow, M, 1996'
     6    return (
     7        f'{first_name}, {last_name},'
     8        ' M, 1996'
     9    )
    10
    11
    12def joe():
    

    the terminal is my friend, and shows TypeError

    TypeError: factory() missing
               1 required positional argument:
               'last_name'
    

    because

  • I add 'doe' to the call to the factory function in test_jane

    36def test_jane():
    37    assert jane() == 'jane, doe, F, 1991'
    38
    39    # reality = factory()
    40    # reality = factory('jane')
    41    reality = factory('jane', 'doe')
    42    my_expectation = 'jane, doe, F, 1991'
    43    assert reality == my_expectation
    44
    45
    46def test_john():
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert 'jane, doe, M, 1996'
                        == 'jane, doe, F, 1991'
    

    the first and last names match. More progress.

  • I change the call to the factory function to use keyword arguments, then add a value for sex (since I have more than two arguments) to in test_jane

    36def test_jane():
    37    assert jane() == 'jane, doe, F, 1991'
    38
    39    # reality = factory()
    40    # reality = factory('jane')
    41    # reality = factory('jane', 'doe')
    42    reality = factory(
    43        first_name='jane',
    44        last_name='doe',
    45        sex='F',
    46    )
    47    my_expectation = 'jane, doe, F, 1991'
    48    assert reality == my_expectation
    49
    50
    51def test_john():
    

    the terminal is my friend, and shows TypeError

    TypeError: factory() got an unexpected
               keyword argument 'sex'
    

    because

  • I add sex in the parentheses of the factory function definition

     1# def factory():
     2# def factory(first_name):
     3# def factory(first_name, last_name):
     4def factory(first_name, last_name, sex):
     5    # return 'joe, blow, M, 1996'
     6    # return f'{first_name}, blow, M, 1996'
     7    return (
     8        f'{first_name}, {last_name},'
     9        ' M, 1996'
    10    )
    11
    12
    13def joe():
    

    the terminal shows the last AssertionError again.

  • I add sex to the f-string in the factory function

     1# def factory():
     2# def factory(first_name):
     3# def factory(first_name, last_name):
     4def factory(first_name, last_name, sex):
     5    # return 'joe, blow, M, 1996'
     6    # return f'{first_name}, blow, M, 1996'
     7    return (
     8        f'{first_name}, {last_name},'
     9        # ' M, 1996'
    10        f' {sex}, 1996'
    11    )
    12
    13
    14def joe():
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert 'jane, doe, F, 1996'
                        == 'jane, doe, F, 1991'
    

    the first name, last name and sex match. Yes!

  • I add year_of_birth to the return statement

     1# def factory():
     2# def factory(first_name):
     3# def factory(first_name, last_name):
     4def factory(first_name, last_name, sex):
     5    # return 'joe, blow, M, 1996'
     6    # return f'{first_name}, blow, M, 1996'
     7    return (
     8        f'{first_name}, {last_name},'
     9        # ' M, 1996'
    10        # f' {sex}, 1996'
    11        f' {sex}, {year_of_birth}'
    12    )
    13
    14
    15def joe():
    

    the terminal is my friend, and shows NameError

    NameError: name 'year_of_birth' is not defined
    
  • I add year_of_birth to the parentheses to define it in the factory function

     1# def factory():
     2# def factory(first_name):
     3# def factory(first_name, last_name):
     4# def factory(first_name, last_name, sex):
     5def factory(
     6    first_name, last_name,
     7    sex, year_of_birth,
     8):
     9    # return 'joe, blow, M, 1996'
    10    # return f'{first_name}, blow, M, 1996'
    11    return (
    12        f'{first_name}, {last_name},'
    13        # ' M, 1996'
    14        # f' {sex}, 1996'
    15        f' {sex}, {year_of_birth}'
    16    )
    17
    18
    19def joe():
    

    the terminal is my friend, and shows TypeError

    TypeError: factory() missing
               1 required positional argument:
               'year_of_birth'
    

    because

  • I add year_of_birth=1996 to the call to the factory function in test_jane

    43def test_jane():
    44    assert jane() == 'jane, doe, F, 1991'
    45
    46    # reality = factory()
    47    # reality = factory('jane')
    48    # reality = factory('jane', 'doe')
    49    reality = factory(
    50        first_name='jane',
    51        last_name='doe',
    52        sex='F',
    53        year_of_birth=1991,
    54    )
    55    my_expectation = 'jane, doe, F, 1991'
    56    assert reality == my_expectation
    57
    58
    59def test_john():
    

    the terminal is my friend, and shows TypeError

    TypeError: factory() missing
               4 required positional arguments:
               'first_name', 'last_name', 'sex',
               and 'year_of_birth'
    

    because

  • Since there are more than two arguments, I add keyword arguments to the call to the factory function in test_joe

    35def test_joe():
    36    assert joe() == 'joe, blow, M, 1996'
    37
    38    # reality = factory()
    39    reality = factory(
    40        first_name='joe',
    41        last_name='blow',
    42        sex='M',
    43        year_of_birth=1996,
    44    )
    45    my_expectation = 'joe, blow, M, 1996'
    46    assert reality == my_expectation
    47
    48
    49def test_jane():
    

    the test passes and I have one function that I can use to make any number of people.


  • I remove the commented lines from the factory function

     1def factory(
     2    first_name, last_name,
     3    sex, year_of_birth,
     4):
     5    return (
     6        f'{first_name}, {last_name},'
     7        f' {sex}, {year_of_birth}'
     8    )
     9
    10
    11def joe():
    
  • I comment out the call to joe in test_joe because I no longer need it since the factory function does the same thing

    27def test_joe():
    28    # assert joe() == 'joe, blow, M, 1996'
    29
    30    reality = factory(
    31        first_name='joe',
    32        last_name='blow',
    33        sex='M',
    34        year_of_birth=1996,
    35    )
    36    my_expectation = 'joe, blow, M, 1996'
    37    assert reality == my_expectation
    38
    39
    40def test_jane():
    
  • I add a variable for 'joe' in test_joe

    27def test_joe():
    28    # assert joe() == 'joe, blow, M, 1996'
    29    first_name = 'joe'
    30
    31    reality = factory(
    32        first_name='joe',
    33        last_name='blow',
    34        sex='M',
    35        year_of_birth=1996,
    36    )
    37    my_expectation = 'joe, blow, M, 1996'
    38    assert reality == my_expectation
    39
    40
    41def test_jane():
    
  • I use the variable to remove repetition of 'joe'

    27def test_joe():
    28    # assert joe() == 'joe, blow, M, 1996'
    29    first_name = 'joe'
    30
    31    reality = factory(
    32        # first_name='joe',
    33        first_name=first_name,
    34        last_name='blow',
    35        sex='M',
    36        year_of_birth=1996,
    37    )
    38    # my_expectation = 'joe, blow, M, 1996'
    39    my_expectation = (
    40        f'{first_name}, blow,'
    41        ' M, 1996'
    42    )
    43    assert reality == my_expectation
    44
    45
    46def test_jane():
    

    the test is still green.

  • I add a variable for 'blow' in test_joe

    27def test_joe():
    28    # assert joe() == 'joe, blow, M, 1996'
    29    first_name = 'joe'
    30    last_name = 'blow'
    31
    32    reality = factory(
    33        # first_name='joe',
    34        first_name=first_name,
    35        last_name='blow',
    36        sex='M',
    37        year_of_birth=1996,
    38    )
    39    # my_expectation = 'joe, blow, M, 1996'
    40    my_expectation = (
    41        f'{first_name}, blow,'
    42        ' M, 1996'
    43    )
    44    assert reality == my_expectation
    45
    46
    47def test_jane():
    
  • I use the variable to remove repetition of 'blow'

    27def test_joe():
    28    # assert joe() == 'joe, blow, M, 1996'
    29    first_name = 'joe'
    30    last_name = 'blow'
    31
    32    reality = factory(
    33        # first_name='joe',
    34        first_name=first_name,
    35        # last_name='blow',
    36        last_name=last_name,
    37        sex='M',
    38        year_of_birth=1996,
    39    )
    40    # my_expectation = 'joe, blow, M, 1996'
    41    my_expectation = (
    42        # f'{first_name}, blow,'
    43        f'{first_name}, {last_name},'
    44        ' M, 1996'
    45    )
    46    assert reality == my_expectation
    47
    48
    49def test_jane():
    

    still green.

  • I add a variable for 'M' in test_joe

    27def test_joe():
    28    # assert joe() == 'joe, blow, M, 1996'
    29    first_name = 'joe'
    30    last_name = 'blow'
    31    sex = 'M'
    32
    33    reality = factory(
    34        # first_name='joe',
    35        first_name=first_name,
    36        # last_name='blow',
    37        last_name=last_name,
    38        sex='M',
    39        year_of_birth=1996,
    40    )
    
  • I use the variable to remove repetition of 'M'

    27def test_joe():
    28    # assert joe() == 'joe, blow, M, 1996'
    29    first_name = 'joe'
    30    last_name = 'blow'
    31    sex = 'M'
    32
    33    reality = factory(
    34        # first_name='joe',
    35        first_name=first_name,
    36        # last_name='blow',
    37        last_name=last_name,
    38        # sex='M',
    39        sex=sex,
    40        year_of_birth=1996,
    41    )
    42    # my_expectation = 'joe, blow, M, 1996'
    43    my_expectation = (
    44        # f'{first_name}, blow,'
    45        f'{first_name}, {last_name},'
    46        # ' M, 1996'
    47        f' {sex}, 1996'
    48    )
    49    assert reality == my_expectation
    50
    51
    52def test_jane():
    

    green.

  • I add a variable for 1996 in test_joe

    27def test_joe():
    28    # assert joe() == 'joe, blow, M, 1996'
    29    first_name = 'joe'
    30    last_name = 'blow'
    31    sex = 'M'
    32    year_of_birth = 1996
    33
    34    reality = factory(
    35        # first_name='joe',
    36        first_name=first_name,
    37        # last_name='blow',
    38        last_name=last_name,
    39        # sex='M',
    40        sex=sex,
    41        year_of_birth=1996,
    42    )
    
  • I use the variable to remove repetition of 1996

    27def test_joe():
    28    # assert joe() == 'joe, blow, M, 1996'
    29    first_name = 'joe'
    30    last_name = 'blow'
    31    sex = 'M'
    32    year_of_birth = 1996
    33
    34    reality = factory(
    35        # first_name='joe',
    36        first_name=first_name,
    37        # last_name='blow',
    38        last_name=last_name,
    39        # sex='M',
    40        sex=sex,
    41        # year_of_birth=1996,
    42        year_of_birth=year_of_birth,
    43    )
    44    # my_expectation = 'joe, blow, M, 1996'
    45    my_expectation = (
    46        # f'{first_name}, blow,'
    47        f'{first_name}, {last_name},'
    48        # ' M, 1996'
    49        # f' {sex}, 1996'
    50        f' {sex}, {year_of_birth}'
    51    )
    52    assert reality == my_expectation
    53
    54
    55def test_jane():
    

    still green.

  • I remove the commented lines

    27def test_joe():
    28    first_name = 'joe'
    29    last_name = 'blow'
    30    sex = 'M'
    31    year_of_birth = 1996
    32
    33    reality = factory(
    34        first_name=first_name,
    35        last_name=last_name,
    36        sex=sex,
    37        year_of_birth=year_of_birth,
    38    )
    39    my_expectation = (
    40        f'{first_name}, {last_name},'
    41        f' {sex}, {year_of_birth}'
    42    )
    43    assert reality == my_expectation
    44
    45
    46def test_jane():
    

  • I comment out the call to jane in test_jane because I no longer need it since the factory function does the same thing

    46def test_jane():
    47    # assert jane() == 'jane, doe, F, 1991'
    48
    49    # reality = factory()
    
  • I add a variable for 'jane' in test_jane

    46def test_jane():
    47    # assert jane() == 'jane, doe, F, 1991'
    48    first_name = 'jane'
    49
    50    # reality = factory()
    
  • I use the variable to remove repetition of 'jane'

    46def test_jane():
    47    # assert jane() == 'jane, doe, F, 1991'
    48    first_name = 'jane'
    49
    50    # reality = factory()
    51    # reality = factory('jane')
    52    # reality = factory('jane', 'doe')
    53    reality = factory(
    54        # first_name='jane',
    55        first_name=first_name,
    56        last_name='doe',
    57        sex='F',
    58        year_of_birth=1991,
    59    )
    60    # my_expectation = 'jane, doe, F, 1991'
    61    my_expectation = (
    62        f'{first_name}, doe,'
    63        ' F, 1991'
    64    )
    65    assert reality == my_expectation
    66
    67
    68def test_john():
    

    the test is still green.

  • I add a variable for 'doe' in test_jane

    46def test_jane():
    47    # assert jane() == 'jane, doe, F, 1991'
    48    first_name = 'jane'
    49    last_name = 'doe'
    50
    51    # reality = factory()
    
  • I use the variable to remove repetition of 'doe'

    46def test_jane():
    47    # assert jane() == 'jane, doe, F, 1991'
    48    first_name = 'jane'
    49    last_name = 'doe'
    50
    51    # reality = factory()
    52    # reality = factory('jane')
    53    # reality = factory('jane', 'doe')
    54    reality = factory(
    55        # first_name='jane',
    56        first_name=first_name,
    57        # last_name='doe',
    58        last_name=last_name,
    59        sex='F',
    60        year_of_birth=1991,
    61    )
    62    # my_expectation = 'jane, doe, F, 1991'
    63    my_expectation = (
    64        # f'{first_name}, doe,'
    65        f'{first_name}, {last_name},'
    66        ' F, 1991'
    67    )
    68    assert reality == my_expectation
    69
    70
    71def test_john():
    

    still green.

  • I add a variable for 'F' in test_jane

    46def test_jane():
    47    # assert jane() == 'jane, doe, F, 1991'
    48    first_name = 'jane'
    49    last_name = 'doe'
    50    sex = 'F'
    51
    52    # reality = factory()
    
  • I use the variable to remove repetition of 'F'

    46def test_jane():
    47    # assert jane() == 'jane, doe, F, 1991'
    48    first_name = 'jane'
    49    last_name = 'doe'
    50    sex = 'F'
    51
    52    # reality = factory()
    53    # reality = factory('jane')
    54    # reality = factory('jane', 'doe')
    55    reality = factory(
    56        # first_name='jane',
    57        first_name=first_name,
    58        # last_name='doe',
    59        last_name=last_name,
    60        # sex='F',
    61        sex=sex,
    62        year_of_birth=1991,
    63    )
    64    # my_expectation = 'jane, doe, F, 1991'
    65    my_expectation = (
    66        # f'{first_name}, doe,'
    67        f'{first_name}, {last_name},'
    68        # ' F, 1991'
    69        f' {sex}, 1991'
    70    )
    71    assert reality == my_expectation
    72
    73
    74def test_john():
    

    green.

  • I add a variable for 1991 in test_jane

    46def test_jane():
    47    # assert jane() == 'jane, doe, F, 1991'
    48    first_name = 'jane'
    49    last_name = 'doe'
    50    sex = 'F'
    51    year_of_birth = 1991
    52
    53    # reality = factory()
    
  • I use the variable to remove repetition of 1991

    46def test_jane():
    47    # assert jane() == 'jane, doe, F, 1991'
    48    first_name = 'jane'
    49    last_name = 'doe'
    50    sex = 'F'
    51    year_of_birth = 1991
    52
    53    # reality = factory()
    54    # reality = factory('jane')
    55    # reality = factory('jane', 'doe')
    56    reality = factory(
    57        # first_name='jane',
    58        first_name=first_name,
    59        # last_name='doe',
    60        last_name=last_name,
    61        # sex='F',
    62        sex=sex,
    63        # year_of_birth=1991,
    64        year_of_birth=year_of_birth,
    65    )
    66    # my_expectation = 'jane, doe, F, 1991'
    67    my_expectation = (
    68        # f'{first_name}, doe,'
    69        f'{first_name}, {last_name},'
    70        # ' F, 1991'
    71        # f' {sex}, 1991'
    72        f' {sex}, {year_of_birth}'
    73    )
    74    assert reality == my_expectation
    75
    76
    77def test_john():
    

    still green.

  • I remove the commented lines

    46def test_jane():
    47    first_name = 'jane'
    48    last_name = 'doe'
    49    sex = 'F'
    50    year_of_birth = 1991
    51
    52    reality = factory(
    53        first_name=first_name,
    54        last_name=last_name,
    55        sex=sex,
    56        year_of_birth=year_of_birth,
    57    )
    58    my_expectation = (
    59        f'{first_name}, {last_name},'
    60        f' {sex}, {year_of_birth}'
    61    )
    62    assert reality == my_expectation
    63
    64
    65def test_john():
    

  • I change the call in test_john to a call to the factory function

    65def test_john():
    66    # assert john() == 'john, smith, M, 1580'
    67    reality = factory(
    68        first_name='john',
    69        last_name='smith',
    70        sex='M',
    71        year_of_birth=1580,
    72    )
    73    my_expectation = 'jane, doe, F, 1991'
    74    assert reality == my_expectation
    75
    76
    77def test_mary():
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert 'john, smith, M, 1580'
                        == 'jane, doe, F, 1991'
    
  • I change my_expectation to match reality

    65def test_john():
    66    # assert john() == 'john, smith, M, 1580'
    67    reality = factory(
    68        first_name='john',
    69        last_name='smith',
    70        sex='M',
    71        year_of_birth=1580,
    72    )
    73    # my_expectation = 'jane, doe, F, 1991'
    74    my_expectation = 'john, smith, M, 1580'
    75    assert reality == my_expectation
    76
    77
    78def test_mary():
    

    the test passes.

  • I add a variable for 'john' in test_john

    65def test_john():
    66    # assert john() == 'john, smith, M, 1580'
    67    first_name = 'john'
    68
    69    reality = factory(
    70        first_name='john',
    71        last_name='smith',
    72        sex='M',
    73        year_of_birth=1580,
    74    )
    75    # my_expectation = 'jane, doe, F, 1991'
    76    my_expectation = 'john, smith, M, 1580'
    77    assert reality == my_expectation
    78
    79
    80def test_mary():
    
  • I use the variable to remove repetition of 'john'

    65def test_john():
    66    # assert john() == 'john, smith, M, 1580'
    67    first_name = 'john'
    68
    69    reality = factory(
    70        # first_name='john',
    71        first_name=first_name,
    72        last_name='smith',
    73        sex='M',
    74        year_of_birth=1580,
    75    )
    76    # my_expectation = 'jane, doe, F, 1991'
    77    # my_expectation = 'john, smith, M, 1580'
    78    my_expectation = (
    79        f'{first_name}, smith,'
    80        ' M, 1580'
    81    )
    82    assert reality == my_expectation
    83
    84
    85def test_mary():
    

    the test is still green.

  • I add a variable for 'smith' in test_john

    65def test_john():
    66    # assert john() == 'john, smith, M, 1580'
    67    first_name = 'john'
    68    last_name = 'smith'
    
  • I use the variable to remove repetition of 'smith'

    65def test_john():
    66    # assert john() == 'john, smith, M, 1580'
    67    first_name = 'john'
    68    last_name = 'smith'
    69
    70    reality = factory(
    71        # first_name='john',
    72        first_name=first_name,
    73        # last_name='smith',
    74        last_name=last_name,
    75        sex='M',
    76        year_of_birth=1580,
    77    )
    78    # my_expectation = 'jane, doe, F, 1991'
    79    # my_expectation = 'john, smith, M, 1580'
    80    my_expectation = (
    81        # f'{first_name}, smith,'
    82        f'{first_name}, {last_name},'
    83        ' M, 1580'
    84    )
    85    assert reality == my_expectation
    86
    87
    88def test_mary():
    

    still green.

  • I add a variable for 'M' in test_john

    65def test_john():
    66    # assert john() == 'john, smith, M, 1580'
    67    first_name = 'john'
    68    last_name = 'smith'
    69    sex = 'M'
    
  • I use the variable to remove repetition of 'M'

    65def test_john():
    66    # assert john() == 'john, smith, M, 1580'
    67    first_name = 'john'
    68    last_name = 'smith'
    69    sex = 'M'
    70
    71    reality = factory(
    72        # first_name='john',
    73        first_name=first_name,
    74        # last_name='smith',
    75        last_name=last_name,
    76        # sex='M',
    77        sex=sex,
    78        year_of_birth=1580,
    79    )
    80    # my_expectation = 'jane, doe, F, 1991'
    81    # my_expectation = 'john, smith, M, 1580'
    82    my_expectation = (
    83        # f'{first_name}, smith,'
    84        f'{first_name}, {last_name},'
    85        # ' M, 1580'
    86        f' {sex}, 1580'
    87    )
    88    assert reality == my_expectation
    89
    90
    91def test_mary():
    

    green.

  • I add a variable for 1580 in test_john

    65def test_john():
    66    # assert john() == 'john, smith, M, 1580'
    67    first_name = 'john'
    68    last_name = 'smith'
    69    sex = 'M'
    70    year_of_birth = 1580
    
  • I use the variable to remove repetition of 1580

    65def test_john():
    66    # assert john() == 'john, smith, M, 1580'
    67    first_name = 'john'
    68    last_name = 'smith'
    69    sex = 'M'
    70    year_of_birth = 1580
    71
    72    reality = factory(
    73        # first_name='john',
    74        first_name=first_name,
    75        # last_name='smith',
    76        last_name=last_name,
    77        # sex='M',
    78        sex=sex,
    79        # year_of_birth=1580,
    80        year_of_birth=year_of_birth,
    81    )
    82    # my_expectation = 'jane, doe, F, 1991'
    83    # my_expectation = 'john, smith, M, 1580'
    84    my_expectation = (
    85        # f'{first_name}, smith,'
    86        f'{first_name}, {last_name},'
    87        # ' M, 1580'
    88        # f' {sex}, 1580'
    89        f' {sex}, {year_of_birth}'
    90    )
    91    assert reality == my_expectation
    92
    93
    94def test_mary():
    

    still green.

  • I remove the commented lines

    65def test_john():
    66    first_name = 'john'
    67    last_name = 'smith'
    68    sex = 'M'
    69    year_of_birth = 1580
    70
    71    reality = factory(
    72        first_name=first_name,
    73        last_name=last_name,
    74        sex=sex,
    75        year_of_birth=year_of_birth,
    76    )
    77    my_expectation = (
    78        f'{first_name}, {last_name},'
    79        f' {sex}, {year_of_birth}'
    80    )
    81    assert reality == my_expectation
    82
    83
    84def test_mary():
    
  • I make the same change to test_mary

     84def test_mary():
     85    # assert mary() == 'mary, public, F, 2000'
     86    first_name = 'mary'
     87    last_name = 'public'
     88    sex = 'F'
     89    year_of_birth = 2000
     90
     91    reality = factory(
     92        first_name=first_name,
     93        last_name=last_name,
     94        sex=sex,
     95        year_of_birth=year_of_birth,
     96    )
     97    my_expectation = (
     98        f'{first_name}, {last_name},'
     99        f' {sex}, {year_of_birth}'
    100    )
    101    assert reality == my_expectation
    102
    103
    104# Exceptions seen
    

    the test is still green.

  • I remove the commented line

     84def test_mary():
     85    first_name = 'mary'
     86    last_name = 'public'
     87    sex = 'F'
     88    year_of_birth = 2000
     89
     90    reality = factory(
     91        first_name=first_name,
     92        last_name=last_name,
     93        sex=sex,
     94        year_of_birth=year_of_birth,
     95    )
     96    my_expectation = (
     97        f'{first_name}, {last_name},'
     98        f' {sex}, {year_of_birth}'
     99    )
    100    assert reality == my_expectation
    101
    102
    103# Exceptions seen
    
  • I remove the joe, jane, john and mary functions because they are no longer used

     1def factory(
     2    first_name, last_name,
     3    sex, year_of_birth,
     4):
     5    return (
     6        f'{first_name}, {last_name},'
     7        f' {sex}, {year_of_birth}'
     8    )
     9
    10
    11def test_joe():
    

    the factory function can make a string for any person I want when I give it the first name, last name, sex and year of birth.

  • I open a new terminal then change directories to person

    cd person
    
  • I add a git commit message in the new terminal

    git commit -am 'extract factory function'
    

RED: make it fail


  • I go back to the terminal where the tests are running

  • I change reality for test_joe to be the result of a call to the factory function of the person module in the src folder instead of a call to the factory function in test_person.py

    11def test_joe():
    12    first_name = 'joe'
    13    last_name = 'blow'
    14    sex = 'M'
    15    year_of_birth = 1996
    16
    17    # reality = factory(
    18    reality = src.person.factory(
    19        first_name=first_name,
    20        last_name=last_name,
    21        sex=sex,
    22        year_of_birth=year_of_birth,
    23    )
    24    my_expectation = (
    25        f'{first_name}, {last_name},'
    26        f' {sex}, {year_of_birth}'
    27    )
    28    assert reality == my_expectation
    29
    30
    31def test_jane():
    

    the terminal is my friend, and shows NameError

    NameError: name 'src' is not defined
    

    because src is not defined in test_person.py


GREEN: make it pass


  • I add an import statement at the top of test_person.py

    1import src.person
    2
    3
    4def factory(
    5    first_name, last_name,
    6    sex, year_of_birth,
    7):
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.person' has no attribute 'factory'
    

    because there is nothing named factory in person.py in the src folder.

  • I add AttributeError to the list of Exceptions seen

    90# Exceptions seen
    91# AssertionError
    92# NameError
    93# TypeError
    94# AttributeError
    
  • I open person/__init__.py from the src folder

  • I delete all the text in the file then add a copy of the factory function to person.py

    1def factory(
    2    first_name, last_name,
    3    sex, year_of_birth,
    4):
    5    return (
    6        f'{first_name}, {last_name},'
    7        f' {sex}, {year_of_birth}'
    8    )
    

    the test passes because

    • Python brings in an object for the person.py file from the src folder so I can use it in test_person.py as src.person when import src.person runs.

    • Python calls the factory function from the object it imported for the person.py file from the src folder (src.person) when src.person.factory is called.

    I think of src.person.factory like an address

    • factory is something in person, in this case it is a function in person

    • person is something in src, in this case it is person.py (a module) in the src folder

    • src is something Python can import (a module, Python package or folder)

      src.person.factory
      src
      └── person.py
          └── def factory(
                  first_name, last_name,
                  sex, year_of_birth,
              ):
              └── return (
                      f'{first_name}, {last_name},'
                      f' {sex}, {year_of_birth}'
                  )
      

REFACTOR: make it better


  • I remove the commented line from test_joe in test_person.py

    14def test_joe():
    15    first_name = 'joe'
    16    last_name = 'blow'
    17    sex = 'M'
    18    year_of_birth = 1996
    19
    20    reality = src.person.factory(
    21        first_name=first_name,
    22        last_name=last_name,
    23        sex=sex,
    24        year_of_birth=year_of_birth,
    25    )
    26    my_expectation = (
    27        f'{first_name}, {last_name},'
    28        f' {sex}, {year_of_birth}'
    29    )
    30    assert reality == my_expectation
    31
    32
    33def test_jane():
    
  • I change reality for test_jane to be the result of a call to the factory function of the person module in the src folder

    33def test_jane():
    34    first_name = 'jane'
    35    last_name = 'doe'
    36    sex = 'F'
    37    year_of_birth = 1991
    38
    39    # reality = factory(
    40    reality = src.person.factory(
    41        first_name=first_name,
    42        last_name=last_name,
    43        sex=sex,
    44        year_of_birth=year_of_birth,
    45    )
    46    my_expectation = (
    47        f'{first_name}, {last_name},'
    48        f' {sex}, {year_of_birth}'
    49    )
    50    assert reality == my_expectation
    51
    52
    53def test_john():
    

    the test is still green.

  • I remove the commented line from test_jane

    33def test_jane():
    34    first_name = 'jane'
    35    last_name = 'doe'
    36    sex = 'F'
    37    year_of_birth = 1991
    38
    39    reality = src.person.factory(
    40        first_name=first_name,
    41        last_name=last_name,
    42        sex=sex,
    43        year_of_birth=year_of_birth,
    44    )
    45    my_expectation = (
    46        f'{first_name}, {last_name},'
    47        f' {sex}, {year_of_birth}'
    48    )
    49    assert reality == my_expectation
    50
    51
    52def test_john():
    
  • I change reality for test_john to be the result of a call to the factory function of the person module in the src folder

    52def test_john():
    53    first_name = 'john'
    54    last_name = 'smith'
    55    sex = 'M'
    56    year_of_birth = 1580
    57
    58    # reality = factory(
    59    reality = src.person.factory(
    60        first_name=first_name,
    61        last_name=last_name,
    62        sex=sex,
    63        year_of_birth=year_of_birth,
    64    )
    65    my_expectation = (
    66        f'{first_name}, {last_name},'
    67        f' {sex}, {year_of_birth}'
    68    )
    69    assert reality == my_expectation
    70
    71
    72def test_mary():
    

    still green.

  • I remove the commented line

    52def test_john():
    53    first_name = 'john'
    54    last_name = 'smith'
    55    sex = 'M'
    56    year_of_birth = 1580
    57
    58    reality = src.person.factory(
    59        first_name=first_name,
    60        last_name=last_name,
    61        sex=sex,
    62        year_of_birth=year_of_birth,
    63    )
    64    my_expectation = (
    65        f'{first_name}, {last_name},'
    66        f' {sex}, {year_of_birth}'
    67    )
    68    assert reality == my_expectation
    69
    70
    71def test_mary():
    
  • I do the same thing to the call to the factory function in test_mary

    71def test_mary():
    72    first_name = 'mary'
    73    last_name = 'public'
    74    sex = 'F'
    75    year_of_birth = 2000
    76
    77    # reality = factory(
    78    reality = src.person.factory(
    79        first_name=first_name,
    80        last_name=last_name,
    81        sex=sex,
    82        year_of_birth=year_of_birth,
    83    )
    84    my_expectation = (
    85        f'{first_name}, {last_name},'
    86        f' {sex}, {year_of_birth}'
    87    )
    88    assert reality == my_expectation
    89
    90
    91# Exceptions seen
    

    green.

  • I remove the commented line

    71def test_mary():
    72    first_name = 'mary'
    73    last_name = 'public'
    74    sex = 'F'
    75    year_of_birth = 2000
    76
    77    reality = src.person.factory(
    78        first_name=first_name,
    79        last_name=last_name,
    80        sex=sex,
    81        year_of_birth=year_of_birth,
    82    )
    83    my_expectation = (
    84        f'{first_name}, {last_name},'
    85        f' {sex}, {year_of_birth}'
    86    )
    87    assert reality == my_expectation
    88
    89
    90# Exceptions seen
    91# AssertionError
    92# NameError
    93# TypeError
    94# AttributeError
    
  • I remove the factory function from test_person.py

     1import src.person
     2
     3
     4def test_joe():
     5    first_name = 'joe'
     6    last_name = 'blow'
     7    sex = 'M'
     8    year_of_birth = 1996
     9
    10    reality = src.person.factory(
    11        first_name=first_name,
    12        last_name=last_name,
    13        sex=sex,
    14        year_of_birth=year_of_birth,
    15    )
    16    my_expectation = (
    17        f'{first_name}, {last_name},'
    18        f' {sex}, {year_of_birth}'
    19    )
    20    assert reality == my_expectation
    21
    22
    23def test_jane():
    

    all the tests are still green because the calls that were made to the factory function that was in test_person.py are now made to the factory function in person.py in the src folder. When src.person.factory is called, Python follows this path

    src.person.factory
    src
    └── person.py
        └── def factory(
                first_name, last_name,
                sex, year_of_birth,
            ):
            └── return (
                    f'{first_name}, {last_name},'
                    f' {sex}, {year_of_birth}'
                )
    
  • I add a git commit message in the other terminal

    git commit -am \
    'move factory function to person.py'
    

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

I can write solutions in a different module from the tests.


test say_hello function

I want the person I make to say hi. I can make a function that takes input about a person and returns a message.


RED: make it fail


  • I add an assertion to test_joe in test_person.py

     1import src.person
     2
     3
     4def test_joe():
     5    first_name = 'joe'
     6    last_name = 'blow'
     7    sex = 'M'
     8    year_of_birth = 1996
     9
    10    reality = src.person.factory(
    11        first_name=first_name,
    12        last_name=last_name,
    13        sex=sex,
    14        year_of_birth=year_of_birth,
    15    )
    16    my_expectation = (
    17        f'{first_name}, {last_name},'
    18        f' {sex}, {year_of_birth}'
    19    )
    20    assert reality == my_expectation
    21
    22    reality = say_hello(
    23        first_name=first_name,
    24        last_name=last_name,
    25        year_of_birth=year_of_birth,
    26    )
    27    my_expectation = (
    28        f'Hello, my name is {first_name}'
    29        f' {last_name} and I am'
    30        f' {2026-year_of_birth}.'
    31    )
    32    assert reality == my_expectation
    33
    34
    35def test_jane():
    

    the terminal is my friend, and shows NameError

    NameError: name 'say_hello' is not defined
    

GREEN: make it pass


  • I add a function definition for it

    1import src.person
    2
    3
    4def say_hello():
    5    return None
    6
    7
    8def test_joe():
    

    the terminal is my friend, and shows TypeError

    TypeError: say_hello() got an
               unexpected keyword argument
               'first_name'
    

    because

  • I add first_name in parentheses

    4# def say_hello():
    5def say_hello(first_name):
    6    return None
    7
    8
    9def test_joe():
    

    the terminal is my friend, and shows TypeError

    TypeError: say_hello()
               got an unexpected keyword argument 'last_name'.
               Did you mean 'first_name'?
    
  • I add last_name in parentheses

     4# def say_hello():
     5# def say_hello(first_name):
     6def say_hello(first_name, last_name):
     7    return None
     8
     9
    10def test_joe():
    

    the terminal is my friend, and shows TypeError

    TypeError: say_hello()
               got an unexpected keyword argument
               'year_of_birth'
    

    because

  • I add year_of_birth in parentheses

     4# def say_hello():
     5# def say_hello(first_name):
     6# def say_hello(first_name, last_name):
     7def say_hello(
     8    first_name, last_name,
     9    year_of_birth
    10):
    11    return None
    12
    13
    14def test_joe():
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert None
        == 'Hello, my name is joe blow and I am 30.'
    
  • I change the return statement to give the test what it wants

     4# def say_hello():
     5# def say_hello(first_name):
     6# def say_hello(first_name, last_name):
     7def say_hello(
     8    first_name, last_name,
     9    year_of_birth
    10):
    11    # return None
    12    return 'Hello, my name is joe blow and I am 30.'
    13
    14
    15def test_joe():
    

    the test passes.


REFACTOR: make it better


  • I add an assertion for the say_hello function to test_jane in test_person.py

    46def test_jane():
    47    first_name = 'jane'
    48    last_name = 'doe'
    49    sex = 'F'
    50    year_of_birth = 1991
    51
    52    reality = src.person.factory(
    53        first_name=first_name,
    54        last_name=last_name,
    55        sex=sex,
    56        year_of_birth=year_of_birth,
    57    )
    58    my_expectation = (
    59        f'{first_name}, {last_name},'
    60        f' {sex}, {year_of_birth}'
    61    )
    62    assert reality == my_expectation
    63
    64    reality = say_hello(
    65        first_name=first_name,
    66        last_name=last_name,
    67        year_of_birth=year_of_birth,
    68    )
    69    my_expectation = (
    70        f'Hello, my name is {first_name}'
    71        f' {last_name} and I am'
    72        f' {2026-year_of_birth}.'
    73    )
    74    assert reality == my_expectation
    75
    76
    77def test_john():
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert 'Hello, my name ... and I am 30.'
                        == 'Hello, my name ... and I am 35.'
    
  • I change the return statement of the say_hello function to an f-string to use first_name in the output

     4# def say_hello():
     5# def say_hello(first_name):
     6# def say_hello(first_name, last_name):
     7def say_hello(
     8    first_name, last_name,
     9    year_of_birth
    10):
    11    # return None
    12    # return 'Hello, my name is joe blow and I am 30.'
    13    return (
    14        f'Hello, my name is {first_name}'
    15        ' blow and I am 30.'
    16    )
    17
    18
    19def test_joe():
    

    the terminal shows AssertionError

    AssertionError: assert 'Hello, my name ... and I am 30.'
                        == 'Hello, my name ... and I am 35.'
    

    the first names now match and I still need better error messages.

  • I add last_name to the return statement

     4# def say_hello():
     5# def say_hello(first_name):
     6# def say_hello(first_name, last_name):
     7def say_hello(
     8    first_name, last_name,
     9    year_of_birth
    10):
    11    # return None
    12    # return 'Hello, my name is joe blow and I am 30.'
    13    return (
    14        f'Hello, my name is {first_name}'
    15        # ' blow and I am 30.'
    16        f' {last_name} and I am 30.'
    17    )
    18
    19
    20def test_joe():
    

    the terminal shows AssertionError

    AssertionError: assert 'Hello, my name ... and I am 30.'
                        == 'Hello, my name ... and I am 35.'
    

    the ages are different.

  • I add the age calculation to the return statement

     4# def say_hello():
     5# def say_hello(first_name):
     6# def say_hello(first_name, last_name):
     7def say_hello(
     8    first_name, last_name,
     9    year_of_birth
    10):
    11    # return None
    12    # return 'Hello, my name is joe blow and I am 30.'
    13    age = 2026 - year_of_birth
    14
    15    return (
    16        f'Hello, my name is {first_name}'
    17        # ' blow and I am 30.'
    18        # f' {last_name} and I am 30.'
    19        f' {last_name} and I am {age}.'
    20    )
    21
    22
    23def test_joe():
    

    the test passes.

  • I add an assertion for the say_hello function to test_john in test_person.py

     85def test_john():
     86    first_name = 'john'
     87    last_name = 'smith'
     88    sex = 'M'
     89    year_of_birth = 1580
     90
     91    reality = src.person.factory(
     92        first_name=first_name,
     93        last_name=last_name,
     94        sex=sex,
     95        year_of_birth=year_of_birth,
     96    )
     97    my_expectation = (
     98        f'{first_name}, {last_name},'
     99        f' {sex}, {year_of_birth}'
    100    )
    101    assert reality == my_expectation
    102
    103    reality = say_hello(
    104        first_name=first_name,
    105        last_name=last_name,
    106        year_of_birth=year_of_birth,
    107    )
    108    my_expectation = (
    109        'Hello, my name is jane'
    110        f' {last_name} and I am'
    111        f' {2026-year_of_birth}.'
    112    )
    113    assert reality == my_expectation
    114
    115
    116def test_mary():
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert 'Hello, my name ...and I am 446.'
                        == 'Hello, my name ...and I am 446.'
    
  • I change my_expectation to match reality in test_john

    103    reality = say_hello(
    104        first_name=first_name,
    105        last_name=last_name,
    106        year_of_birth=year_of_birth,
    107    )
    108    my_expectation = (
    109        # 'Hello, my name is jane'
    110        f'Hello, my name is {first_name}'
    111        f' {last_name} and I am'
    112        f' {2026-year_of_birth}.'
    113    )
    114    assert reality == my_expectation
    115
    116
    117def test_mary():
    

    the test passes.

  • I add an assertion for the say_hello function to test_mary

    117def test_mary():
    118    first_name = 'mary'
    119    last_name = 'public'
    120    sex = 'F'
    121    year_of_birth = 2000
    122
    123    reality = src.person.factory(
    124        first_name=first_name,
    125        last_name=last_name,
    126        sex=sex,
    127        year_of_birth=year_of_birth,
    128    )
    129    my_expectation = (
    130        f'{first_name}, {last_name},'
    131        f' {sex}, {year_of_birth}'
    132    )
    133    assert reality == my_expectation
    134
    135    reality = say_hello(
    136        first_name=first_name,
    137        last_name=last_name,
    138        year_of_birth=year_of_birth,
    139    )
    140    my_expectation = (
    141        f'Hello, my name is {first_name}'
    142        ' smith and I am'
    143        f' {2026-year_of_birth}.'
    144    )
    145    assert reality == my_expectation
    146
    147
    148# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert 'Hello, my name ... and I am 26.'
                        == 'Hello, my name ... and I am 26.'
    
    • The last names are different.

    • I want better messages that show as much of the difference in the summary.

  • I change my_expectation to match reality in test_mary

    135    reality = say_hello(
    136        first_name=first_name,
    137        last_name=last_name,
    138        year_of_birth=year_of_birth,
    139    )
    140    my_expectation = (
    141        f'Hello, my name is {first_name}'
    142        # ' smith and I am'
    143        f' {last_name} and I am'
    144        f' {2026-year_of_birth}.'
    145    )
    146    assert reality == my_expectation
    147
    148
    149# Exceptions seen
    

    the test passes.

  • I remove the commented line

    116def test_mary():
    117    first_name = 'mary'
    118    last_name = 'public'
    119    sex = 'F'
    120    year_of_birth = 2000
    121
    122    reality = src.person.factory(
    123        first_name=first_name,
    124        last_name=last_name,
    125        sex=sex,
    126        year_of_birth=year_of_birth,
    127    )
    128    my_expectation = (
    129        f'{first_name}, {last_name},'
    130        f' {sex}, {year_of_birth}'
    131    )
    132    assert reality == my_expectation
    133
    134    reality = say_hello(
    135        first_name=first_name,
    136        last_name=last_name,
    137        year_of_birth=year_of_birth,
    138    )
    139    my_expectation = (
    140        f'Hello, my name is {first_name}'
    141        f' {last_name} and I am'
    142        f' {2026-year_of_birth}.'
    143    )
    144    assert reality == my_expectation
    145
    146
    147# Exceptions seen
    

    the test passes because Python uses the string representation of the object in the curly braces { }

    say_hello(
        first_name=first_name,
        last_name=last_name,
        year_of_birth=year_of_birth,
    )
        say_hello(
            first_name, last_name,
            year_of_birth,
        )
            first_name = 'mary'
            last_name = 'public'
            year_of_birth = 1991
    
            age = 2026 - year_of_birth
            age = 2026 - 1991
            age = 35
    
            return (
                f'Hello, my name is {first_name}'
                f' {last_name} and I am {age}.'
            )
            return f'Hello, my name is mary public and I am 35'
    

separate and equal say_hello

  • I change reality to be the result of a call to the say_hello function of the person module in the src folder instead of a call to the say_hello function in test_person.py

    117def test_mary():
    118    first_name = 'mary'
    119    last_name = 'public'
    120    sex = 'F'
    121    year_of_birth = 2000
    122
    123    reality = src.person.factory(
    124        first_name=first_name,
    125        last_name=last_name,
    126        sex=sex,
    127        year_of_birth=year_of_birth,
    128    )
    129    my_expectation = (
    130        f'{first_name}, {last_name},'
    131        f' {sex}, {year_of_birth}'
    132    )
    133    assert reality == my_expectation
    134
    135    # reality = say_hello(
    136    reality = src.person.say_hello(
    137        first_name=first_name,
    138        last_name=last_name,
    139        year_of_birth=year_of_birth,
    140    )
    141    my_expectation = (
    142        f'Hello, my name is {first_name}'
    143        # ' smith and I am'
    144        f' {last_name} and I am'
    145        f' {2026-year_of_birth}.'
    146    )
    147    assert reality == my_expectation
    148
    149
    150# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.person'
                    has no attribute 'say_hello'
    

    because there is nothing named say_hello in the person.py file in the src folder.

  • I add a copy of the say_hello function without the commented lines to person.py

     1def factory(
     2    first_name, last_name,
     3    sex, year_of_birth,
     4):
     5    return (
     6        f'{first_name}, {last_name},'
     7        f' {sex}, {year_of_birth}'
     8    )
     9
    10
    11def say_hello(
    12    first_name, last_name,
    13    year_of_birth
    14):
    15    age = 2026 - year_of_birth
    16
    17    return (
    18        f'Hello, my name is {first_name}'
    19        f' {last_name} and I am {age}.'
    20    )
    

    the test passes.

  • I remove the commented lines from test_mary in test_person.py

    117def test_mary():
    118    first_name = 'mary'
    119    last_name = 'public'
    120    sex = 'F'
    121    year_of_birth = 2000
    122
    123    reality = src.person.factory(
    124        first_name=first_name,
    125        last_name=last_name,
    126        sex=sex,
    127        year_of_birth=year_of_birth,
    128    )
    129    my_expectation = (
    130        f'{first_name}, {last_name},'
    131        f' {sex}, {year_of_birth}'
    132    )
    133    assert reality == my_expectation
    134
    135    reality = src.person.say_hello(
    136        first_name=first_name,
    137        last_name=last_name,
    138        year_of_birth=year_of_birth,
    139    )
    140    my_expectation = (
    141        f'Hello, my name is {first_name}'
    142        f' {last_name} and I am'
    143        f' {2026-year_of_birth}.'
    144    )
    145    assert reality == my_expectation
    146
    147
    148# Exceptions seen
    149# AssertionError
    150# NameError
    151# TypeError
    152# AttributeError
    
  • I change reality for test_john to be the result of a call to the say_hello function of the person module in the src folder

     85def test_john():
     86    first_name = 'john'
     87    last_name = 'smith'
     88    sex = 'M'
     89    year_of_birth = 1580
     90
     91    reality = src.person.factory(
     92        first_name=first_name,
     93        last_name=last_name,
     94        sex=sex,
     95        year_of_birth=year_of_birth,
     96    )
     97    my_expectation = (
     98        f'{first_name}, {last_name},'
     99        f' {sex}, {year_of_birth}'
    100    )
    101    assert reality == my_expectation
    102
    103    # reality = say_hello(
    104    reality = src.person.say_hello(
    105        first_name=first_name,
    106        last_name=last_name,
    107        year_of_birth=year_of_birth,
    108    )
    109    my_expectation = (
    110        # 'Hello, my name is jane'
    111        f'Hello, my name is {first_name}'
    112        f' {last_name} and I am'
    113        f' {2026-year_of_birth}.'
    114    )
    115    assert reality == my_expectation
    116
    117
    118def test_mary():
    

    the test is still green.

  • I remove the commented lines from test_john

     85def test_john():
     86    first_name = 'john'
     87    last_name = 'smith'
     88    sex = 'M'
     89    year_of_birth = 1580
     90
     91    reality = src.person.factory(
     92        first_name=first_name,
     93        last_name=last_name,
     94        sex=sex,
     95        year_of_birth=year_of_birth,
     96    )
     97    my_expectation = (
     98        f'{first_name}, {last_name},'
     99        f' {sex}, {year_of_birth}'
    100    )
    101    assert reality == my_expectation
    102
    103    reality = src.person.say_hello(
    104        first_name=first_name,
    105        last_name=last_name,
    106        year_of_birth=year_of_birth,
    107    )
    108    my_expectation = (
    109        f'Hello, my name is {first_name}'
    110        f' {last_name} and I am'
    111        f' {2026-year_of_birth}.'
    112    )
    113    assert reality == my_expectation
    114
    115
    116def test_mary():
    
  • I change reality for test_joe to be the result of a call to the say_hello function of the person module in the src folder

    23def test_joe():
    24    first_name = 'joe'
    25    last_name = 'blow'
    26    sex = 'M'
    27    year_of_birth = 1996
    28
    29    reality = src.person.factory(
    30        first_name=first_name,
    31        last_name=last_name,
    32        sex=sex,
    33        year_of_birth=year_of_birth,
    34    )
    35    my_expectation = (
    36        f'{first_name}, {last_name},'
    37        f' {sex}, {year_of_birth}'
    38    )
    39    assert reality == my_expectation
    40
    41    # reality = say_hello(
    42    reality = src.person.say_hello(
    43        first_name=first_name,
    44        last_name=last_name,
    45        year_of_birth=year_of_birth,
    46    )
    47    my_expectation = (
    48        f'Hello, my name is {first_name}'
    49        f' {last_name} and I am'
    50        f' {2026-year_of_birth}.'
    51    )
    52    assert reality == my_expectation
    53
    54
    55def test_jane():
    

    the test is still green.

  • I remove the commented line from test_joe

    23def test_joe():
    24    first_name = 'joe'
    25    last_name = 'blow'
    26    sex = 'M'
    27    year_of_birth = 1996
    28
    29    reality = src.person.factory(
    30        first_name=first_name,
    31        last_name=last_name,
    32        sex=sex,
    33        year_of_birth=year_of_birth,
    34    )
    35    my_expectation = (
    36        f'{first_name}, {last_name},'
    37        f' {sex}, {year_of_birth}'
    38    )
    39    assert reality == my_expectation
    40
    41    reality = src.person.say_hello(
    42        first_name=first_name,
    43        last_name=last_name,
    44        year_of_birth=year_of_birth,
    45    )
    46    my_expectation = (
    47        f'Hello, my name is {first_name}'
    48        f' {last_name} and I am'
    49        f' {2026-year_of_birth}.'
    50    )
    51    assert reality == my_expectation
    52
    53
    54def test_jane():
    
  • I change reality for test_jane to be the result of a call to the say_hello function of the person module in the src folder

    54def test_jane():
    55    first_name = 'jane'
    56    last_name = 'doe'
    57    sex = 'F'
    58    year_of_birth = 1991
    59
    60    reality = src.person.factory(
    61        first_name=first_name,
    62        last_name=last_name,
    63        sex=sex,
    64        year_of_birth=year_of_birth,
    65    )
    66    my_expectation = (
    67        f'{first_name}, {last_name},'
    68        f' {sex}, {year_of_birth}'
    69    )
    70    assert reality == my_expectation
    71
    72    # reality = say_hello(
    73    reality = src.person.say_hello(
    74        first_name=first_name,
    75        last_name=last_name,
    76        year_of_birth=year_of_birth,
    77    )
    78    my_expectation = (
    79        f'Hello, my name is {first_name}'
    80        f' {last_name} and I am'
    81        f' {2026-year_of_birth}.'
    82    )
    83    assert reality == my_expectation
    84
    85
    86def test_john():
    

    the test is still green.

  • I remove the commented line from test_jane

    54def test_jane():
    55    first_name = 'jane'
    56    last_name = 'doe'
    57    sex = 'F'
    58    year_of_birth = 1991
    59
    60    reality = src.person.factory(
    61        first_name=first_name,
    62        last_name=last_name,
    63        sex=sex,
    64        year_of_birth=year_of_birth,
    65    )
    66    my_expectation = (
    67        f'{first_name}, {last_name},'
    68        f' {sex}, {year_of_birth}'
    69    )
    70    assert reality == my_expectation
    71
    72    reality = src.person.say_hello(
    73        first_name=first_name,
    74        last_name=last_name,
    75        year_of_birth=year_of_birth,
    76    )
    77    my_expectation = (
    78        f'Hello, my name is {first_name}'
    79        f' {last_name} and I am'
    80        f' {2026-year_of_birth}.'
    81    )
    82    assert reality == my_expectation
    83
    84
    85def test_john():
    
  • I remove the say_hello function from test_person.py

    1import src.person
    2
    3
    4def test_joe():
    

    all the tests are still green because the calls that were made to the say_hello function that was in test_person.py are now made to the say_hello function in person.py in the src folder. When src.person.say_hello is called, Python follows this path

    src.person.say_hello
    src
    └── person.py
        └── def say_hello(
                first_name, last_name,
                year_of_birth
            ):
            ├── age = 2026 - year_of_birth
                    └── return (
                    f'Hello, my name is {first_name}'
                    f' {last_name} and I am {age}.'
                )
    
  • I add a git commit message in the other terminal

    git commit -am 'add say_hello function'
    

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


test_person

Since the solutions are separate from the tests, I can write the programs that make the tests pass without looking at test_person.py.


RED: make it fail


  • I close test_person.py

  • I delete all the text in person.py and the terminal shows 4 failures. I start with the last AttributeError

    FAILED ...::test_joe - AttributeError:
        module 'src.person' has no attribute 'factory'
    FAILED ...::test_jane - AttributeError:
        module 'src.person' has no attribute 'factory'
    FAILED ...::test_john - AttributeError:
        module 'src.person' has no attribute 'factory'
    FAILED ...::test_mary - AttributeError:
        module 'src.person' has no attribute 'factory'
    =================== 4 failed in A.BCs ===================
    

    Can you make the tests pass without looking at how I solve it below? You can come back to compare solutions when you are done or if you get stuck.


GREEN: make it pass


  • I add the name to person.py

    1factory
    

    the terminal is my friend, and shows NameError

    NameError: name 'factory' is not defined
    
  • I point factory to None (the simplest object) to define it

    1# factory
    2factory = None
    

    the terminal is my friend, and shows TypeError

    TypeError: 'NoneType' object is not callable
    

    because factory points to None and I cannot call None like a function.

  • I change factory to a function to make it callable

    1# factory
    2# factory = None
    3def factory():
    4    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: factory() got
               an unexpected keyword argument
               'first_name'
    

    because this function definition does not allow any inputs, the parentheses are empty.

  • I add first_name in the parentheses so the function can take input

    1# factory
    2# factory = None
    3# def factory():
    4def factory(first_name):
    5    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: factory() got
               an unexpected keyword argument 'last_name'.
               Did you mean 'first_name'?
    

    because the function definition now only allows one input (first_name) and it was called with a keyword argument (last_name).

  • I add last_name to the parentheses

    1# factory
    2# factory = None
    3# def factory():
    4# def factory(first_name):
    5def factory(first_name, last_name):
    6    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: factory() got
               an unexpected keyword argument 'sex'
    

    because the function definition only allows two inputs (first_name and last_name) and it was called with a keyword argument (sex).

  • I add sex to the parentheses

    1# factory
    2# factory = None
    3# def factory():
    4# def factory(first_name):
    5# def factory(first_name, last_name):
    6def factory(first_name, last_name, sex):
    7    return None
    

    the terminal is my friend, and shows TypeError

    TypeError: factory() got
               an unexpected keyword argument
               'year_of_birth'
    

    because the function definition only allows three inputs (first_name, last_name, sex) and it was called with a keyword argument (year_of_birth).

  • I add year_of_birth to the parentheses

     1# factory
     2# factory = None
     3# def factory():
     4# def factory(first_name):
     5# def factory(first_name, last_name):
     6# def factory(first_name, last_name, sex):
     7def factory(
     8        first_name, last_name,
     9        sex, year_of_birth,
    10    ):
    11    return None
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert None == 'mary, public, F, 2000'
    
  • I change the return statement to match the expectation of the test

     1# factory
     2# factory = None
     3# def factory():
     4# def factory(first_name):
     5# def factory(first_name, last_name):
     6# def factory(first_name, last_name, sex):
     7def factory(
     8        first_name, last_name,
     9        sex, year_of_birth,
    10    ):
    11    return 'mary, public, F, 2000'
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.person'
                    has no attribute 'say_hello'
    
  • I add the name

     1say_hello
     2
     3
     4# factory
     5# factory = None
     6# def factory():
     7# def factory(first_name):
     8# def factory(first_name, last_name):
     9# def factory(first_name, last_name, sex):
    10def factory(
    11        first_name, last_name,
    12        sex, year_of_birth,
    13    ):
    14    return 'mary, public, F, 2000'
    

    the terminal is my friend, and shows NameError

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

    1# say_hello
    2say_hello = None
    3
    4
    5# factory
    

    the terminal is my friend, and shows TypeError

    TypeError: 'NoneType' object is not callable
    

    because say_hello points to None and I cannot call None like a function.

  • I make say_hello a function to make it callable

    1# say_hello
    2# say_hello = None
    3def say_hello():
    4    return None
    5
    6
    7# factory
    

    the terminal is my friend, and shows TypeError

    TypeError: say_hello() got
               an unexpected keyword argument
               'first_name'
    

    because this function definition does not allow any inputs, the parentheses are empty.

  • I add first_name in the parentheses

    1# say_hello
    2# say_hello = None
    3# def say_hello():
    4def say_hello(first_name):
    5    return None
    6
    7
    8# factory
    

    the terminal is my friend, and shows TypeError

    TypeError: say_hello() got
               an unexpected keyword argument 'last_name'.
               Did you mean 'first_name'?
    

    because the function definition now only allows one input (first_name) and it was called with a keyword argument (last_name).

  • I add last_name to the parentheses

    1# say_hello
    2# say_hello = None
    3# def say_hello():
    4# def say_hello(first_name):
    5def say_hello(first_name, last_name):
    6    return None
    7
    8
    9# factory
    

    the terminal is my friend, and shows TypeError

    TypeError: say_hello() got
               an unexpected keyword argument
               'year_of_birth'
    

    because the function definition only allows two inputs (first_name and last_name) and it was called with a keyword argument (year_of_birth).

  • I add year_of_birth to the parentheses

     1# say_hello
     2# say_hello = None
     3# def say_hello():
     4# def say_hello(first_name):
     5# def say_hello(first_name, last_name):
     6def say_hello(
     7    first_name, last_name, year_of_birth,
     8):
     9    return None
    10
    11
    12# factory
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert None
        == 'Hello, my name is mary public and I am 26.'
    
  • I use ctrl/command+c (Windows & Linux/MacOS) on the keyboard to copy the string from the terminal and ctrl/command+v to paste it to replace None in the return statement

     1# say_hello
     2# say_hello = None
     3# def say_hello():
     4# def say_hello(first_name):
     5# def say_hello(first_name, last_name):
     6def say_hello(
     7    first_name, last_name, year_of_birth,
     8):
     9    # return None
    10    return 'Hello, my name is mary public and I am 26.'
    11
    12
    13# factory
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert 'mary, public, F, 2000'
                        == 'john, smith, M, 1580'
    

    because the factory function always returns 'mary, public, F, 2000' and this test expects 'john, smith, M, 1580'.

  • I change the return statement of the factory function to see the difference between the input and the expected output (remember the identity function?)

    13# factory
    14# factory = None
    15# def factory():
    16# def factory(first_name):
    17# def factory(first_name, last_name):
    18# def factory(first_name, last_name, sex):
    19def factory(
    20        first_name, last_name,
    21        sex, year_of_birth,
    22    ):
    23    # return 'mary, public, F, 2000'
    24    return first_name, last_name, sex, year_of_birth,
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert ('mary', 'public', 'F', 2000)
                         == 'mary, public, F, 2000'
    

    the first_name, last_name, sex and year_of_birth are part of the output.

  • I change the return statement to an f-string with those values

    13# factory
    14# factory = None
    15# def factory():
    16# def factory(first_name):
    17# def factory(first_name, last_name):
    18# def factory(first_name, last_name, sex):
    19def factory(
    20        first_name, last_name,
    21        sex, year_of_birth,
    22    ):
    23    # return 'mary, public, F, 2000'
    24    # return first_name, last_name, sex, year_of_birth,
    25    return (
    26        f'{first_name}, {last_name},'
    27        f' {sex}, {year_of_birth}'
    28    )
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert 'Hello, my name ... and I am 26.'
                        == 'Hello, my name ...and I am 446.'
    

    because the say_hello function always returns 'Hello, my name is mary public and I am 26.' and this test expects 'Hello, my name is john smith and I am 446.'

  • I change the return statement of the say_hello function to see the difference between the input and the expected output

     1# say_hello
     2# say_hello = None
     3# def say_hello():
     4# def say_hello(first_name):
     5# def say_hello(first_name, last_name):
     6def say_hello(
     7    first_name, last_name, year_of_birth,
     8):
     9    # return None
    10    # return 'Hello, my name is mary public and I am 26.'
    11    return first_name, last_name, year_of_birth,
    12
    13
    14# factory
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert ('mary', 'public', 'F', 2000)
                         == 'mary, public, F, 2000'
    

    the first_name and last_name are part of the output.

  • I change the return statement to an f-string with the input values

     1# say_hello
     2# say_hello = None
     3# def say_hello():
     4# def say_hello(first_name):
     5# def say_hello(first_name, last_name):
     6def say_hello(
     7    first_name, last_name, year_of_birth,
     8):
     9    # return None
    10    # return 'Hello, my name is mary public and I am 26.'
    11    # return first_name, last_name, year_of_birth,
    12    return (
    13        f'Hello, my name is {first_name}'
    14        f' {last_name} and I am {year_of_birth}.'
    15    )
    16
    17
    18# factory
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert 'Hello, my name ...nd I am 2000.'
                        == 'Hello, my name ... and I am 26.'
    

    It looks like 26 is the age.

  • I change the year_of_birth value to a calculation of the age

     1# say_hello
     2# say_hello = None
     3# def say_hello():
     4# def say_hello(first_name):
     5# def say_hello(first_name, last_name):
     6def say_hello(
     7    first_name, last_name, year_of_birth,
     8):
     9    # return None
    10    # return 'Hello, my name is mary public and I am 26.'
    11    # return first_name, last_name, year_of_birth,
    12    return (
    13        f'Hello, my name is {first_name}'
    14        # f' {last_name} and I am {year_of_birth}.'
    15        f' {last_name} and I am'
    16        f' {2026-year_of_birth}.'
    17    )
    18
    19
    20# factory
    

    all tests are green!

  • I remove the commented lines

     1def say_hello(
     2    first_name, last_name, year_of_birth,
     3):
     4    return (
     5        f'Hello, my name is {first_name}'
     6        f' {last_name} and I am'
     7        f' {2026-year_of_birth}.'
     8    )
     9
    10
    11def factory(
    12        first_name, last_name,
    13        sex, year_of_birth,
    14    ):
    15    return (
    16        f'{first_name}, {last_name},'
    17        f' {sex}, {year_of_birth}'
    18    )
    
  • I add a git commit message in the new terminal

    git commit -am 'test person with f-strings'
    

close the project

  • I close person.py

  • I click in the terminal where the tests are running

  • I use q on the keyboard to leave the tests. The terminal goes back to the command line.

  • I change directory to the parent of person

    cd ..
    

    the terminal shows

    ...\pumping_python
    

    I am back in the pumping_python directory.


review

  • I ran tests to write one function that makes a person when given first_name, last_name, sex and year_of_birth so I do not have to make one function for each person.

  • I also ran tests to make another function that uses f-strings to make a string that represents the person I make saying hi when I give it first_name, last_name, and year_of_birth.

  • I saw the following Exceptions

  • My tests and solutions have a few problems,

    • Each test is basically the same two tests, there has to be a way that I can use one test for all the people.

    • The factory and say_hello functions use three of the same inputs

      • first_name

      • last_name

      • year_of_birth

      There has to be a better way<how to make a person with a class>, where I can give those values once, and get a representation for a person when I call the factory function and a message when I call the say_hello function.

For now, I am going to clean up the functions project so the tests and solutions are in separate files.


code from the chapter

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


what is next?

Would you like to see me separate the tests and functions in the functions project?


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.