how to make a person with f-strings
Since I can pass objects to a string with f-strings, I can write one function that makes a person instead of one function for each person.
preview
I have these tests by the end of the chapter
1import src.person
2
3
4def assert_say_hello_works(
5 first_name, last_name,
6 year_of_birth,
7 ):
8 reality = src.person.say_hello(
9 first_name=first_name,
10 last_name=last_name,
11 year_of_birth=year_of_birth,
12 )
13 my_expectation = (
14 f'Hello, my name is {first_name}'
15 f' {last_name} and I am'
16 f' {2026-year_of_birth}.'
17 )
18 assert reality == my_expectation
21def assert_person_factory_works(
22 first_name, last_name,
23 sex, year_of_birth
24 ):
25 reality = src.person.person(
26 first_name=first_name,
27 last_name=last_name,
28 sex=sex,
29 year_of_birth=year_of_birth,
30 )
31 my_expectation = (
32 f'{first_name}, {last_name},'
33 f' {sex}, {year_of_birth}'
34 )
35 assert reality == my_expectation
38def test_joe():
39 first_name = 'joe'
40 last_name = 'blow'
41 sex = 'M'
42 year_of_birth = 1996
43
44 assert_person_factory_works(
45 first_name=first_name,
46 last_name=last_name,
47 sex=sex,
48 year_of_birth=year_of_birth,
49 )
50
51 assert_say_hello_works(
52 first_name=first_name,
53 last_name=last_name,
54 year_of_birth=year_of_birth,
55 )
58def test_jane():
59 first_name = 'jane'
60 last_name = 'doe'
61 sex = 'F'
62 year_of_birth = 1991
63
64 assert_person_factory_works(
65 first_name=first_name,
66 last_name=last_name,
67 sex=sex,
68 year_of_birth=year_of_birth,
69 )
70
71 assert_say_hello_works(
72 first_name=first_name,
73 last_name=last_name,
74 year_of_birth=year_of_birth,
75 )
78def test_john():
79 first_name = 'john'
80 last_name = 'smith'
81 sex = 'M'
82 year_of_birth = 1580
83
84 assert_person_factory_works(
85 first_name=first_name,
86 last_name=last_name,
87 sex=sex,
88 year_of_birth=year_of_birth,
89 )
90
91 assert_say_hello_works(
92 first_name=first_name,
93 last_name=last_name,
94 year_of_birth=year_of_birth,
95 )
98def test_mary():
99 first_name = 'mary'
100 last_name = 'public'
101 sex = 'F'
102 year_of_birth = 2000
103
104 assert_person_factory_works(
105 first_name=first_name,
106 last_name=last_name,
107 sex=sex,
108 year_of_birth=year_of_birth,
109 )
110
111 assert_say_hello_works(
112 first_name=first_name,
113 last_name=last_name,
114 year_of_birth=year_of_birth,
115 )
116
117
118# Exceptions seen
119# AssertionError
120# NameError
121# TypeError
122# AttributeError
open the project
I open a terminal
I change directory to the project
cd personthe terminal shows I am in the
personfolder.../pumping_python/personI open
test_person.pyfrom thetestsfolderI use pytest-watcher to run the tests automatically
uv run pytest-watcher . --nowthe terminal shows the tests from before are passing.
extract person function
RED: make it fail
I add an assertion to test_joe in tests/test_person.py
17 def test_joe():
18 assert joe() == 'joe, blow, M, 1996'
19
20 reality = person()
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 'person' is not defined
because I have not defined person in tests/test_person.py, yet.
GREEN: make it pass
I add a function definition for it
1def person():
2 return 'joe, blow, M, 1996'
3
4
5def joe():
the test passes.
person() -> 'joe, blow, M, 1996'
└── def person():
└── return 'joe, blow, M, 1996'
REFACTOR: make it better
I add an assertion with a call to the person function from test_jane
21def test_joe(): 22 assert joe() == 'joe, blow, M, 1996' 23 24 reality = person() 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 = person() 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 person function always returns
joe, blow, M, 1996when it is called. I want it 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 person(): 2def person(first_name): 3 return 'joe, blow, M, 1996'the terminal is my friend, and shows TypeError
TypeError: person() missing 1 required positional argument: 'first_name'because
I called the person function with zero inputs.
The function definition (signature) of
personhas one required argument (first_name).The call to a function must match its signature (definition).
I add
'jane'to the call to the person function from test_jane30def test_jane(): 31 assert jane() == 'jane, doe, F, 1991' 32 33 # reality = person() 34 reality = person('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 person function to an f-string to use
first_namein the output1# def person(): 2def person(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_nameto the return statement1# def person(): 2def person(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 definedI add the name to the parentheses to define it in the person function
1# def person(): 2# def person(first_name): 3def person(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: person() missing 1 required positional argument: 'last_name'because
I called the person function with one input (
jane).The function definition (signature) of
personhas two required arguments (first_nameandlast_name).The call to a function must match its signature (definition).
I add
'doe'to the call to the person function in test_jane36def test_jane(): 37 assert jane() == 'jane, doe, F, 1991' 38 39 # reality = person() 40 # reality = person('jane') 41 reality = person('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 person function to use keyword arguments, then add a value for
sex(since I have more than two arguments) in test_jane36def test_jane(): 37 assert jane() == 'jane, doe, F, 1991' 38 39 # reality = person() 40 # reality = person('jane') 41 # reality = person('jane', 'doe') 42 reality = person( 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: person() got an unexpected keyword argument 'sex'because
I called the person function with three keyword arguments input (
first_name,last_nameandsex).The function definition (signature) of
personhas two required arguments (first_nameandlast_name).The call to a function must match its signature (definition).
I add
sexin the parentheses of thepersonfunction definition1# def person(): 2# def person(first_name): 3# def person(first_name, last_name): 4def person(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 still shows AssertionError.
I add
sexto the f-string in the person function1# def person(): 2# def person(first_name): 3# def person(first_name, last_name): 4def person(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_birthto the return statement1# def person(): 2# def person(first_name): 3# def person(first_name, last_name): 4def person(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 definedI add
year_of_birthto the parentheses to define it in the person function1# def person(): 2# def person(first_name): 3# def person(first_name, last_name): 4# def person(first_name, last_name, sex): 5def person( 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: person() missing 1 required positional argument: 'year_of_birth'because
I called the person function with three keyword arguments (
first_name,last_nameandsex).The function definition (signature) of
personhas four required arguments (first_name,last_name,sexandyear_of_birth).The call to a function must match its signature (definition).
I add
year_of_birth=1991to the call to the person function from test_jane43def test_jane(): 44 assert jane() == 'jane, doe, F, 1991' 45 46 # reality = person() 47 # reality = person('jane') 48 # reality = person('jane', 'doe') 49 reality = person( 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: person() missing 4 required positional arguments: 'first_name', 'last_name', 'sex', and 'year_of_birth'because
I called the person function from test_joe with zero arguments.
The function definition (signature) of
personhas four required arguments (first_name,last_name,sexandyear_of_birth).The call to a function must match its signature (definition).
Since there are more than two arguments, I add keyword arguments to the call to the person function from test_joe
35def test_joe(): 36 assert joe() == 'joe, blow, M, 1996' 37 38 # reality = person() 39 reality = person( 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. I have one function that I can use to make any number of people.
person( first_name='joe', last_name='blow', sex='M', year_of_birth=1996, ) -> 'joe, blow, M, 1996' └── def person( first_name, last_name, sex, year_of_birth, ): ├── first_name = 'joe' ├── last_name = 'blow' ├── sex = 'M' ├── year_of_birth = 1996 └── return ( f'{first_name}, {last_name},' f' {sex}, {year_of_birth}' ) return 'joe, blow, M, 1996'person( first_name='jane', last_name='doe', sex='F', year_of_birth=1991, ) -> 'jane, doe, F, 1991' └── def person( first_name, last_name, sex, year_of_birth, ): ├── first_name = 'jane' ├── last_name = 'doe' ├── sex = 'F' ├── year_of_birth = 1991 └── return ( f'{first_name}, {last_name},' f' {sex}, {year_of_birth}' ) return 'jane, doe, F, 1991'
I remove the commented lines from the person function
1def person( 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
joein test_joe because I no longer need it since the person function does the same thing27def test_joe(): 28 # assert joe() == 'joe, blow, M, 1996' 29 30 # reality = person()I add a variable for
'joe'in test_joe27def test_joe(): 28 # assert joe() == 'joe, blow, M, 1996' 29 first_name = 'joe' 30 31 # reality = person()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 = person() 32 reality = person( 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, 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_joe27def test_joe(): 28 # assert joe() == 'joe, blow, M, 1996' 29 first_name = 'joe' 30 last_name = 'blow' 31 32 # reality = person()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 = person() 33 reality = person( 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 ) 41 # my_expectation = 'joe, blow, M, 1996' 42 my_expectation = ( 43 # f'{first_name}, blow,' 44 f'{first_name}, {last_name},' 45 ' M, 1996' 46 ) 47 assert reality == my_expectation 48 49 50def test_jane():still green.
I add a variable for
'M'in test_joe27def test_joe(): 28 # assert joe() == 'joe, blow, M, 1996' 29 first_name = 'joe' 30 last_name = 'blow' 31 sex = 'M' 32 33 # reality = person()I use the variable to remove repetition of
'M'from test_joe27def test_joe(): 28 # assert joe() == 'joe, blow, M, 1996' 29 first_name = 'joe' 30 last_name = 'blow' 31 sex = 'M' 32 33 # reality = person() 34 reality = person( 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 ) 43 # my_expectation = 'joe, blow, M, 1996' 44 my_expectation = ( 45 # f'{first_name}, blow,' 46 f'{first_name}, {last_name},' 47 # ' M, 1996' 48 f' {sex}, 1996' 49 ) 50 assert reality == my_expectation 51 52 53def test_jane():green.
I add a variable for
1996in test_joe27def 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 = person()I use the variable to remove repetition of
1996from test_joe27def 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 = person() 35 reality = person( 36 # first_name='joe', 37 first_name=first_name, 38 # last_name='blow', 39 last_name=last_name, 40 # sex='M', 41 sex=sex, 42 # year_of_birth=1996, 43 year_of_birth=year_of_birth, 44 ) 45 # my_expectation = 'joe, blow, M, 1996' 46 my_expectation = ( 47 # f'{first_name}, blow,' 48 f'{first_name}, {last_name},' 49 # ' M, 1996' 50 # f' {sex}, 1996' 51 f' {sex}, {year_of_birth}' 52 ) 53 assert reality == my_expectation 54 55 56def test_jane():still green.
I remove the commented lines from test_joe
27def test_joe(): 28 first_name = 'joe' 29 last_name = 'blow' 30 sex = 'M' 31 year_of_birth = 1996 32 33 reality = person( 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
janein test_jane because I no longer need it since the person function does the same thing46def test_jane(): 47 # assert jane() == 'jane, doe, F, 1991' 48 49 # reality = person()I add a variable for
'jane'in test_jane46def test_jane(): 47 # assert jane() == 'jane, doe, F, 1991' 48 first_name = 'jane' 49 50 # reality = person()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 = person() 51 # reality = person('jane') 52 # reality = person('jane', 'doe') 53 reality = person( 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_jane46def test_jane(): 47 # assert jane() == 'jane, doe, F, 1991' 48 first_name = 'jane' 49 last_name = 'doe' 50 51 # reality = person()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 = person() 52 # reality = person('jane') 53 # reality = person('jane', 'doe') 54 reality = person( 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_jane46def test_jane(): 47 # assert jane() == 'jane, doe, F, 1991' 48 first_name = 'jane' 49 last_name = 'doe' 50 sex = 'F' 51 52 # reality = person()I use the variable to remove repetition of
'F'from test_jane46def test_jane(): 47 # assert jane() == 'jane, doe, F, 1991' 48 first_name = 'jane' 49 last_name = 'doe' 50 sex = 'F' 51 52 # reality = person() 53 # reality = person('jane') 54 # reality = person('jane', 'doe') 55 reality = person( 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
1991in test_jane46def 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 = person()I use the variable to remove repetition of
199146def 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 = person() 54 # reality = person('jane') 55 # reality = person('jane', 'doe') 56 reality = person( 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 from test_jane
46def test_jane(): 47 first_name = 'jane' 48 last_name = 'doe' 49 sex = 'F' 50 year_of_birth = 1991 51 52 reality = person( 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 person function
65def test_john(): 66 # assert john() == 'john, smith, M, 1580' 67 reality = person( 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_expectationto matchreality65def test_john(): 66 # assert john() == 'john, smith, M, 1580' 67 reality = person( 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.
person( first_name='john', last_name='smith', sex='M', year_of_birth=1580, ) -> 'john, smith, M, 1580' └── def person( first_name, last_name, sex, year_of_birth, ): ├── first_name = 'john' ├── last_name = 'smith' ├── sex = 'M' ├── year_of_birth = 1580 └── return ( f'{first_name}, {last_name},' f' {sex}, {year_of_birth}' ) return 'john, smith, M, 1580'I add a variable for
'john'in test_john65def test_john(): 66 # assert john() == 'john, smith, M, 1580' 67 first_name = 'john' 68 69 reality = person( 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 = person( 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_john65def 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 = person( 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_john65def 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'from test_john65def test_john(): 66 # assert john() == 'john, smith, M, 1580' 67 first_name = 'john' 68 last_name = 'smith' 69 sex = 'M' 70 71 reality = person( 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
1580in test_john65def test_john(): 66 # assert john() == 'john, smith, M, 1580' 67 first_name = 'john' 68 last_name = 'smith' 69 sex = 'M' 70 year_of_birth = 1580I use the variable to remove repetition of
158065def 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 = person( 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 from test_john
65def test_john(): 66 first_name = 'john' 67 last_name = 'smith' 68 sex = 'M' 69 year_of_birth = 1580 70 71 reality = person( 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 = person( 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 seenthe terminal is my friend, and shows AssertionError
AssertionError: assert 'mary, public, F, 2000' != 'mary, public, F, 2000'I change the assertion
97 my_expectation = ( 98 f'{first_name}, {last_name},' 99 f' {sex}, {year_of_birth}' 100 ) 101 # assert reality != my_expectation 102 assert reality == my_expectation 103 104 105# Exceptions seenthe test passes.
person( first_name='mary', last_name='public', sex='F', year_of_birth=2000, ) -> 'mary, public, F, 2000' └── def person( first_name, last_name, sex, year_of_birth, ): ├── first_name = 'mary' ├── last_name = 'public' ├── sex = 'F' ├── year_of_birth = 2000 └── return ( f'{first_name}, {last_name},' f' {sex}, {year_of_birth}' ) return 'mary, public, F, 2000'I remove the commented lines from test_mary
84def test_mary(): 85 first_name = 'mary' 86 last_name = 'public' 87 sex = 'F' 88 year_of_birth = 2000 89 90 reality = person( 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 seenI remove the
joe,jane,johnandmaryfunctions because they are no longer used1def person( 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 person 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
personcd personI add a git commit message in the new terminal
git commit -am 'extract person function'
extract assert_person_factory_works function
test_joe, test_jane, test_john and test_mary are the same process, they
make variables for
first_name,last_name,sexandyear_of_birthcall the person function with the variables
assert that the result of the call to the person function with the variables is equal to the string
reality = person(
first_name=first_name,
last_name=last_name,
sex=sex,
year_of_birth=year_of_birth,
)
my_expectation = (
f'{first_name}, {last_name},'
f' {sex}, {year_of_birth}'
)
assert reality == my_expectation
I can make a function that takes in first_name, last_name, sex and year_of_birth then asserts that the result of the call to the person function with the variables is equal to the string with the values of the given parameters.
RED: make it fail
I go back to the terminal where the tests are running
I add a new function to assert that the result of the call to the person function with the variables is equal to the string with the values of the given parameters
1def person( 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 assert_person_factory_works( 12 first_name, last_name, 13 sex, year_of_birth, 14 ): 15 reality = person( 16 first_name=first_name, 17 last_name=last_name, 18 sex=sex, 19 year_of_birth=year_of_birth, 20 ) 21 my_expectation = ( 22 f'{first_name}, {last_name},' 23 f' {sex}, {year_of_birth}' 24 ) 25 assert reality != my_expectation 26 27 28def test_joe():I use the assert_person_factory_works function in test_joe
28def test_joe(): 29 first_name = 'joe' 30 last_name = 'blow' 31 sex = 'M' 32 year_of_birth = 1996 33 34 # reality = person( 35 # first_name=first_name, 36 # last_name=last_name, 37 # sex=sex, 38 # year_of_birth=year_of_birth, 39 # ) 40 # my_expectation = ( 41 # f'{first_name}, {last_name},' 42 # f' {sex}, {year_of_birth}' 43 # ) 44 # assert reality == my_expectation 45 assert_person_factory_works( 46 first_name=first_name, 47 last_name=last_name, 48 sex=sex, 49 year_of_birth=year_of_birth, 50 ) 51 52 53def test_jane():the terminal is my friend, and shows AssertionError
AssertionError: assert 'joe, blow, M, 1996' != 'joe, blow, M, 1996'
GREEN: make it pass
I change the assertion in the assert_person_factory_works function
11def assert_person_factory_works(
12 first_name, last_name,
13 sex, year_of_birth,
14 ):
15 reality = person(
16 first_name=first_name,
17 last_name=last_name,
18 sex=sex,
19 year_of_birth=year_of_birth,
20 )
21 my_expectation = (
22 f'{first_name}, {last_name},'
23 f' {sex}, {year_of_birth}'
24 )
25 # assert reality != my_expectation
26 assert reality == my_expectation
27
28
29def test_joe():
the test passes.
assert_person_factory_works(
first_name=first_name, last_name=last_name,
sex=sex, year_of_birth=year_of_birth,
) -> None
└── def assert_person_factory_works(
first_name, last_name,
sex, year_of_birth,
):
├── reality = person(
│ first_name=first_name,
│ last_name=last_name,
│ sex=sex,
│ year_of_birth=year_of_birth,
│ )
│ └── def person(
│ first_name, last_name,
│ sex, year_of_birth,
│ ):
│ └── return (
│ f'{first_name}, {last_name},'
│ f' {sex}, {year_of_birth}'
│ )
├── my_expectation = (
│ f'{first_name}, {last_name},'
│ f' {sex}, {year_of_birth}'
│ )
└── assert reality == my_expectation
REFACTOR: make it better
I remove the commented line from assert_person_factory_works
11def assert_person_factory_works( 12 first_name, last_name, 13 sex, year_of_birth, 14 ): 15 reality = person( 16 first_name=first_name, 17 last_name=last_name, 18 sex=sex, 19 year_of_birth=year_of_birth, 20 ) 21 my_expectation = ( 22 f'{first_name}, {last_name},' 23 f' {sex}, {year_of_birth}' 24 ) 25 assert reality == my_expectation 26 27 28def test_joe():I remove the commented lines from test_joe
28def test_joe(): 29 first_name = 'joe' 30 last_name = 'blow' 31 sex = 'M' 32 year_of_birth = 1996 33 34 assert_person_factory_works( 35 first_name=first_name, 36 last_name=last_name, 37 sex=sex, 38 year_of_birth=year_of_birth, 39 ) 40 41 42def test_jane():I use the assert_person_factory_works function in test_jane
42def test_jane(): 43 first_name = 'jane' 44 last_name = 'doe' 45 sex = 'F' 46 year_of_birth = 1991 47 48 # reality = person( 49 # first_name=first_name, 50 # last_name=last_name, 51 # sex=sex, 52 # year_of_birth=year_of_birth, 53 # ) 54 # my_expectation = ( 55 # f'{first_name}, {last_name},' 56 # f' {sex}, {year_of_birth}' 57 # ) 58 # assert reality == my_expectation 59 assert_person_factory_works( 60 first_name=first_name, 61 last_name=last_name, 62 sex=sex, 63 year_of_birth, 64 ) 65 66 67def test_john():the test is still green.
I remove the commented lines from test_jane
42def test_jane(): 43 first_name = 'jane' 44 last_name = 'doe' 45 sex = 'F' 46 year_of_birth = 1991 47 48 assert_person_factory_works( 49 first_name=first_name, 50 last_name=last_name, 51 sex=sex, 52 year_of_birth, 53 ) 54 55 56def test_john():I use the assert_person_factory_works function in test_john
56def test_john(): 57 first_name = 'john' 58 last_name = 'smith' 59 sex = 'M' 60 year_of_birth = 1580 61 62 # reality = person( 63 # first_name=first_name, 64 # last_name=last_name, 65 # sex=sex, 66 # year_of_birth=year_of_birth, 67 # ) 68 # my_expectation = ( 69 # f'{first_name}, {last_name},' 70 # f' {sex}, {year_of_birth}' 71 # ) 72 # assert reality == my_expectation 73 assert_person_factory_works( 74 first_name=first_name, 75 last_name=last_name, 76 sex=sex, 77 year_of_birth=year_of_birth, 78 ) 79 80 81def test_mary():still green.
I remove the commented lines from test_john
56def test_john(): 57 first_name = 'john' 58 last_name = 'smith' 59 sex = 'M' 60 year_of_birth = 1580 61 62 assert_person_factory_works( 63 first_name=first_name, 64 last_name=last_name, 65 sex=sex, 66 year_of_birth=year_of_birth, 67 ) 68 69 70def test_mary():I use the assert_person_factory_works function in test_mary
70def test_mary(): 71 first_name = 'mary' 72 last_name = 'public' 73 sex = 'F' 74 year_of_birth = 2000 75 76 # reality = person( 77 # first_name=first_name, 78 # last_name=last_name, 79 # sex=sex, 80 # year_of_birth=year_of_birth, 81 # ) 82 # my_expectation = ( 83 # f'{first_name}, {last_name},' 84 # f' {sex}, {year_of_birth}' 85 # ) 86 # assert reality == my_expectation 87 assert_person_factory_works( 88 first_name=first_name, 89 last_name=last_name, 90 sex=sex, 91 year_of_birth=year_of_birth, 92 ) 93 94 95# Exceptions seengreen.
I remove the commented lines from test_mary
70def test_mary(): 71 first_name = 'mary' 72 last_name = 'public' 73 sex = 'F' 74 year_of_birth = 2000 75 76 assert_person_factory_works( 77 first_name=first_name, 78 last_name=last_name, 79 sex=sex, 80 year_of_birth=year_of_birth, 81 ) 82 83 84# Exceptions seenI add a git commit message in the other terminal
git commit -am \ 'extract assert_person_factory_works function'
separate and equal person
I want to move the person function to separate it from the tests.
RED: make it fail
I go back to the terminal where the tests are running
I change
realityin the assert_person_factory_works function to be the result of a call to the person function of thepersonmodule in thesrcfolder instead of a call to the person function intests/test_person.py11def assert_person_factory_works( 12 first_name, last_name, 13 sex, year_of_birth, 14 ): 15 # reality = person( 16 reality = src.person.person( 17 first_name=first_name, 18 last_name=last_name, 19 sex=sex, 20 year_of_birth=year_of_birth, 21 ) 22 my_expectation = ( 23 f'{first_name}, {last_name},' 24 f' {sex}, {year_of_birth}' 25 ) 26 assert reality == my_expectation 27 28 29def test_joe():the terminal is my friend, and shows NameError
NameError: name 'src' is not definedbecause
srcis not defined intests/test_person.py.
GREEN: make it pass
I add an import statement at the top of
tests/test_person.py1import src.person 2 3 4def person( 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 'person'because there is nothing named
personin thepersonmodule in thesrcfolder.I add AttributeError to the list of Exceptions seen
88# Exceptions seen 89# AssertionError 90# NameError 91# TypeError 92# AttributeErrorI delete all the text in the file then add a copy of the person function to
src/person/__init__.py1def person( 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 )all the tests are green again because
when
import src.personruns Python brings in an object for the__init__.pyfile from thepersonfolder in thesrcfolder so I can use it intests/test_person.pyassrc.person.Python calls the person function from the object it imported for the
src/person/__init__.pyfile (src.person) whensrc.person.personis called.
I think of
src.person.personlike an addresspersonis something in__init__.pyin thepersonfolder, in this case it is a function inpersonpersonis something in thesrcfolder, in this case it is a folder in thesrcfoldersrcis something Python can import (a module, Python package or folder)src.person.person └── src/ └── person/ └── __init__.py └── def person( 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 assert_person_factory_works in
tests/test_person.py14def assert_person_factory_works( 15 first_name, last_name, 16 sex, year_of_birth, 17 ): 18 reality = src.person.person( 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_joe():I remove the person function from
tests/test_person.py1import 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.person( 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
I add a git commit message in the other terminal
git commit -am \ 'move person function to src'the terminal shows a summary of the changes then goes back to the command line.
test say_hello function
I want the person I make to say hello. I can make a function that takes input for a person and returns a message.
RED: make it fail
I add an assertion to test_joe in
tests/test_person.py21def test_joe(): 22 first_name = 'joe' 23 last_name = 'blow' 24 sex = 'M' 25 year_of_birth = 1996 26 27 assert_person_factory_works( 28 first_name=first_name, 29 last_name=last_name, 30 sex=sex, 31 year_of_birth=year_of_birth, 32 ) 33 34 reality = say_hello( 35 first_name=first_name, 36 last_name=last_name, 37 year_of_birth=year_of_birth, 38 ) 39 my_expectation = ( 40 f'Hello, my name is {first_name}' 41 f' {last_name} and I am' 42 f' {2026-year_of_birth}.' 43 ) 44 assert reality == my_expectation 45 46 47def 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 say_hello
1import src.person 2 3 4def say_hello(): 5 return None 6 7 8def assert_person_factory_works( 9 first_name, last_name, 10 sex, year_of_birth, 11 ):the terminal is my friend, and shows TypeError
TypeError: say_hello() got an unexpected keyword argument 'first_name'because
I called the say_hello function with three keyword arguments (
first_name,last_nameandyear_of_birth).The function definition (signature) of
say_helloallows zero input, because the parentheses are empty.The call to a function must match its signature (definition).
I add
first_namein parentheses4# def say_hello(): 5def say_hello(first_name): 6 return Nonethe 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_namein parentheses4# def say_hello(): 5# def say_hello(first_name): 6def say_hello(first_name, last_name): 7 return Nonethe terminal is my friend, and shows TypeError
TypeError: say_hello() got an unexpected keyword argument 'year_of_birth'because the function definition (signature) of
persononly allows two arguments (first_name,last_name).I add
year_of_birthin parentheses4# 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 Nonethe 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 assert_person_factory_works( 16 first_name, last_name, 17 sex, year_of_birth, 18 ):the test passes.
├── first_name = 'joe' ├── last_name = 'blow' ├── year_of_birth = 1996 └── say_hello( first_name=first_name, last_name=last_name, year_of_birth=year_of_birth, ) -> 'Hello, my name is joe blow and I am 30.' └── def say_hello( first_name, last_name, year_of_birth, ): └── return 'Hello, my name is joe blow and I am 30.'
REFACTOR: make it better
I add an assertion for the say_hello function to test_jane
58def test_jane(): 59 first_name = 'jane' 60 last_name = 'doe' 61 sex = 'F' 62 year_of_birth = 1991 63 64 assert_person_factory_works( 65 first_name=first_name, 66 last_name=last_name, 67 sex=sex, 68 year_of_birth=year_of_birth, 69 ) 70 71 reality = say_hello( 72 first_name=first_name, 73 last_name=last_name, 74 year_of_birth=year_of_birth, 75 ) 76 my_expectation = ( 77 f'Hello, my name is {first_name}' 78 f' {last_name} and I am' 79 f' {2026-year_of_birth}.' 80 ) 81 assert reality == my_expectation 82 83 84def 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.'because the say_hello function always returns
'Hello, my name is joe blow and I am 30.'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 messages based on the person.
I change the return statement of the say_hello function to an f-string to use
first_namein the output4# 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 )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_nameto the return statement4# 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 )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 )the test passes.
├── first_name = 'jane' ├── last_name = 'doe' ├── year_of_birth = 1991 └── say_hello( first_name=first_name, last_name=last_name, year_of_birth=year_of_birth, ) -> 'Hello, my name is jane doe and I am 35.' └── def say_hello( first_name, last_name, year_of_birth, ): ├── first_name = 'jane' ├── last_name = 'doe' ├── year_of_birth = 1991 ├── age = 2026 - year_of_birth │ = 35 └── return ( f'Hello, my name is {first_name}' f' {last_name} and I am {age}.' ) return 'Hello, my name is jane doe and I am 35.'I remove the commented lines from the say_hello function
1import src.person 2 3 4def say_hello( 5 first_name, last_name, 6 year_of_birth, 7 ): 8 age = 2026 - year_of_birth 9 10 return ( 11 f'Hello, my name is {first_name}' 12 f' {last_name} and I am {age}.' 13 ) 14 15 16def assert_person_factory_works( 17 first_name, last_name, 18 sex, year_of_birth, 19 ):I add an assertion for the say_hello function to test_john in
tests/test_person.py85def test_john(): 86 first_name = 'john' 87 last_name = 'smith' 88 sex = 'M' 89 year_of_birth = 1580 90 91 assert_person_factory_works( 92 first_name=first_name, 93 last_name=last_name, 94 sex=sex, 95 year_of_birth=year_of_birth, 96 ) 97 98 reality = say_hello( 99 first_name=first_name, 100 last_name=last_name, 101 year_of_birth=year_of_birth, 102 ) 103 my_expectation = ( 104 'Hello, my name is jane' 105 f' {last_name} and I am' 106 f' {2026-year_of_birth}.' 107 ) 108 assert reality == my_expectation 109 110 111def 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.'The first names are different.
I change
my_expectationto matchrealityin test_john103 my_expectation = ( 104 # 'Hello, my name is jane' 105 f'Hello, my name is {first_name}' 106 f' {last_name} and I am' 107 f' {2026-year_of_birth}.' 108 ) 109 assert reality == my_expectation 110 111 112def test_mary():the test passes.
├── first_name = 'john' ├── last_name = 'smith' ├── year_of_birth = 1580 └── say_hello( first_name=first_name, last_name=last_name, year_of_birth=year_of_birth, ) -> 'Hello, my name is john smith and I am 446.' └── def say_hello( first_name, last_name, year_of_birth, ): ├── first_name = 'john' ├── last_name = 'smith' ├── year_of_birth = 1580 ├── age = 2026 - year_of_birth │ = 446 └── return ( f'Hello, my name is {first_name}' f' {last_name} and I am {age}.' ) return 'Hello, my name is john smith and I am 446.'I add an assertion for the say_hello function to test_mary
112def test_mary(): 113 first_name = 'mary' 114 last_name = 'public' 115 sex = 'F' 116 year_of_birth = 2000 117 118 assert_person_factory_works( 119 first_name=first_name, 120 last_name=last_name, 121 sex=sex, 122 year_of_birth=year_of_birth, 123 ) 124 125 reality = say_hello( 126 first_name=first_name, 127 last_name=last_name, 128 year_of_birth=year_of_birth, 129 ) 130 my_expectation = ( 131 f'Hello, my name is {first_name}' 132 ' smith and I am' 133 f' {2026-year_of_birth}.' 134 ) 135 assert reality == my_expectation 136 137 138# Exceptions seenthe 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 all of the differences in the summary.
I change
my_expectationto matchrealityin test_mary130 my_expectation = ( 131 f'Hello, my name is {first_name}' 132 # ' smith and I am' 133 f' {last_name} and I am' 134 f' {2026-year_of_birth}.' 135 ) 136 assert reality == my_expectation 137 138 139# Exceptions seenthe test passes.
├── first_name = 'mary' ├── last_name = 'public' ├── year_of_birth = 2000 └── say_hello( first_name=first_name, last_name=last_name, year_of_birth=year_of_birth, ) -> 'Hello, my name is mary public and I am 26.' └── def say_hello( first_name, last_name, year_of_birth, ): ├── first_name = 'mary' ├── last_name = 'public' ├── year_of_birth = 2000 ├── age = 2026 - year_of_birth │ = 26 └── return ( f'Hello, my name is {first_name}' f' {last_name} and I am {age}.' ) return 'Hello, my name is mary public and I am 26.'I add a git commit message in the other terminal
git commit -am 'test say_hello function'
extract assert_say_hello_works function
test_joe, test_jane, test_john and test_mary use the same process to test the say_hello function, they
call the say_hello function with values for
first_name,last_nameandyear_of_birthmake a string with the values of
first_name,last_nameandyear_of_birthassert that the result of the call to the say_hello function with
first_name,last_nameandyear_of_birthis equal to the string
reality = say_hello(
first_name=first_name,
last_name=last_name,
year_of_birth=year_of_birth,
)
my_expectation = (
f'Hello, my name is {first_name}'
f' {last_name} and I am'
f' {2026-year_of_birth}.'
)
assert reality == my_expectation
I can make a function that takes in first_name, last_name and year_of_birth then asserts that the result of the call to the say_hello function with the values is equal to the string with the values of the given parameters.
RED: make it fail
I go back to the terminal where the tests are running
I add a new function to assert that the result of the call to the say_hello function with the variables is equal to the string with the values of the given parameters
4def say_hello( 5 first_name, last_name, 6 year_of_birth, 7 ): 8 age = 2026 - year_of_birth 9 10 return ( 11 f'Hello, my name is {first_name}' 12 f' {last_name} and I am {age}.' 13 ) 14 15 16def assert_say_hello_works( 17 first_name, last_name, 18 year_of_birth, 19 ): 20 reality = say_hello( 21 first_name=first_name, 22 last_name=last_name, 23 year_of_birth=year_of_birth, 24 ) 25 my_expectation = ( 26 f'Hello, my name is {first_name}' 27 f' {last_name} and I am' 28 f' {2026-year_of_birth}.' 29 ) 30 assert reality != my_expectation 31 32 33def assert_say_hello_works( 34 first_name, last_name, 35 sex, year_of_birth, 36 ):I use the assert_say_hello_works function in test_joe
50def test_joe(): 51 first_name = 'joe' 52 last_name = 'blow' 53 sex = 'M' 54 year_of_birth = 1996 55 56 assert_person_factory_works( 57 first_name=first_name, 58 last_name=last_name, 59 sex=sex, 60 year_of_birth=year_of_birth, 61 ) 62 63 # reality = say_hello( 64 # first_name=first_name, 65 # last_name=last_name, 66 # year_of_birth=year_of_birth, 67 # ) 68 # my_expectation = ( 69 # f'Hello, my name is {first_name}' 70 # f' {last_name} and I am' 71 # f' {2026-year_of_birth}.' 72 # ) 73 # assert reality == my_expectation 74 assert_say_hello_works( 75 first_name=first_name, 76 last_name=last_name, 77 year_of_birth=year_of_birth, 78 ) 79 80 81def test_jane():the terminal is my friend, and shows AssertionError
AssertionError: assert 'Hello, my name is joe blow and I am 30.' != 'Hello, my name is joe blow and I am 30.'
GREEN: make it pass
I change the assertion in the assert_say_hello_works function
16def assert_say_hello_works(
17 first_name, last_name,
18 year_of_birth,
19 ):
20 reality = say_hello(
21 first_name=first_name,
22 last_name=last_name,
23 year_of_birth=year_of_birth,
24 )
25 my_expectation = (
26 f'Hello, my name is {first_name}'
27 f' {last_name} and I am'
28 f' {2026-year_of_birth}.'
29 )
30 # assert reality != my_expectation
31 assert reality == my_expectation
32
33
34def assert_person_factory_works(
35 first_name, last_name,
36 sex, year_of_birth,
37 ):
the test passes.
assert_say_hello_works(
first_name=first_name,
last_name=last_name,
year_of_birth=year_of_birth,
) -> None
└── def assert_say_hello_works(
first_name, last_name,
sex, year_of_birth,
):
├── reality = say_hello(
│ first_name=first_name,
│ last_name=last_name,
│ year_of_birth=year_of_birth,
│ )
│ └── 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}.'
│ )
├── my_expectation = (
│ f'Hello, my name is {first_name}'
│ f' {last_name} and I am'
│ f' {2026-year_of_birth}.'
│ )
└── assert reality == my_expectation
REFACTOR: make it better
I remove the commented line from assert_say_hello_works
16def assert_say_hello_works( 17 first_name, last_name, 18 year_of_birth, 19 ): 20 reality = say_hello( 21 first_name=first_name, 22 last_name=last_name, 23 year_of_birth=year_of_birth, 24 ) 25 my_expectation = ( 26 f'Hello, my name is {first_name}' 27 f' {last_name} and I am' 28 f' {2026-year_of_birth}.' 29 ) 30 assert reality == my_expectation 31 32 33def assert_person_factory_works( 34 first_name, last_name, 35 sex, year_of_birth, 36 ):I remove the commented lines from test_joe
50def test_joe(): 51 first_name = 'joe' 52 last_name = 'blow' 53 sex = 'M' 54 year_of_birth = 1996 55 56 assert_person_factory_works( 57 first_name=first_name, 58 last_name=last_name, 59 sex=sex, 60 year_of_birth=year_of_birth, 61 ) 62 63 assert_say_hello_works( 64 first_name=first_name, 65 last_name=last_name, 66 year_of_birth=year_of_birth, 67 ) 68 69 70def test_jane():I use the assert_say_hello_works function in test_jane
70def test_jane(): 71 first_name = 'jane' 72 last_name = 'doe' 73 sex = 'F' 74 year_of_birth = 1991 75 76 assert_person_factory_works( 77 first_name=first_name, 78 last_name=last_name, 79 sex=sex, 80 year_of_birth=year_of_birth, 81 ) 82 83 # reality = say_hello( 84 # first_name=first_name, 85 # last_name=last_name, 86 # year_of_birth=year_of_birth, 87 # ) 88 # my_expectation = ( 89 # f'Hello, my name is {first_name}' 90 # f' {last_name} and I am' 91 # f' {2026-year_of_birth}.' 92 # ) 93 # assert reality == my_expectation 94 assert_say_hello_works( 95 first_name=first_name, 96 last_name=last_name, 97 year_of_birth=year_of_birth, 98 ) 99 100 101def test_john():the test is still green.
I remove the commented lines from test_jane
70def test_jane(): 71 first_name = 'jane' 72 last_name = 'doe' 73 sex = 'F' 74 year_of_birth = 1991 75 76 assert_person_factory_works( 77 first_name=first_name, 78 last_name=last_name, 79 sex=sex, 80 year_of_birth=year_of_birth, 81 ) 82 83 assert_say_hello_works( 84 first_name=first_name, 85 last_name=last_name, 86 year_of_birth=year_of_birth, 87 ) 88 89 90def test_john():I use the assert_say_hello_works function in test_john
90def test_john(): 91 first_name = 'john' 92 last_name = 'smith' 93 sex = 'M' 94 year_of_birth = 1580 95 96 assert_person_factory_works( 97 first_name=first_name, 98 last_name=last_name, 99 sex=sex, 100 year_of_birth=year_of_birth, 101 ) 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 # # f'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 assert_say_hello_works( 116 first_name=first_name, 117 last_name=last_name, 118 year_of_birth=year_of_birth, 119 ) 120 121 122def test_mary():still green.
I remove the commented lines from test_john
90def test_john(): 91 first_name = 'john' 92 last_name = 'smith' 93 sex = 'M' 94 year_of_birth = 1580 95 96 assert_person_factory_works( 97 first_name=first_name, 98 last_name=last_name, 99 sex=sex, 100 year_of_birth=year_of_birth, 101 ) 102 103 assert_say_hello_works( 104 first_name=first_name, 105 last_name=last_name, 106 year_of_birth=year_of_birth, 107 ) 108 109 110def test_mary():I use the assert_say_hello_works function in test_mary
110def test_mary(): 111 first_name = 'mary' 112 last_name = 'public' 113 sex = 'F' 114 year_of_birth = 2000 115 116 assert_person_factory_works( 117 first_name=first_name, 118 last_name=last_name, 119 sex=sex, 120 year_of_birth=year_of_birth, 121 ) 122 123 # reality = say_hello( 124 # first_name=first_name, 125 # last_name=last_name, 126 # year_of_birth=year_of_birth, 127 # ) 128 # my_expectation = ( 129 # f'Hello, my name is {first_name}' 130 # # ' smith and I am' 131 # f' {last_name} and I am' 132 # f' {2026-year_of_birth}.' 133 # ) 134 # assert reality == my_expectation 135 assert_say_hello_works( 136 first_name=first_name, 137 last_name=last_name, 138 year_of_birth=year_of_birth, 139 ) 140 141 142# Exceptions seengreen.
I remove the commented lines from test_mary
110def test_mary(): 111 first_name = 'mary' 112 last_name = 'public' 113 sex = 'F' 114 year_of_birth = 2000 115 116 assert_person_factory_works( 117 first_name=first_name, 118 last_name=last_name, 119 sex=sex, 120 year_of_birth=year_of_birth, 121 ) 122 123 assert_say_hello_works( 124 first_name=first_name, 125 last_name=last_name, 126 year_of_birth=year_of_birth, 127 ) 128 129 130# Exceptions seen 131# AssertionError 132# NameError 133# TypeError 134# AttributeErrorI add a git commit message in the other terminal
git commit -am \ 'extract assert_say_hello_works function'
separate and equal say_hello
I want to move the say_hello function to separate it from the tests.
RED: make it fail
I go back to the terminal where the tests are running
I change
realityin the assert_say_hello_works function to be the result of a call to the say_hello function of thepersonmodule in thesrcfolder instead of a call to the say_hello function intests/test_person.py16def assert_say_hello_works( 17 first_name, last_name, 18 year_of_birth, 19 ): 20 # reality = say_hello( 21 reality = src.person.say_hello( 22 first_name=first_name, 23 last_name=last_name, 24 year_of_birth=year_of_birth, 25 ) 26 my_expectation = ( 27 f'Hello, my name is {first_name}' 28 f' {last_name} and I am' 29 f' {2026-year_of_birth}.' 30 ) 31 assert reality == my_expectation 32 33 34def assert_person_factory_works( 35 first_name, last_name, 36 sex, year_of_birth, 37 ):the terminal is my friend, and shows AttributeError
AttributeError: module 'src.person' has no attribute 'say_hello'because there is nothing named
say_helloinsrc/person/__init__.py.
GREEN: make it pass
I add a copy of the say_hello function to src/person/__init__.py
1def person(
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.
src.person.say_hello
└── src/
└── person/
└── __init__.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 remove the commented line from assert_say_hello_works in
tests/test_person.py16def assert_say_hello_works( 17 first_name, last_name, 18 year_of_birth, 19 ): 20 reality = src.person.say_hello( 21 first_name=first_name, 22 last_name=last_name, 23 year_of_birth=year_of_birth, 24 ) 25 my_expectation = ( 26 f'Hello, my name is {first_name}' 27 f' {last_name} and I am' 28 f' {2026-year_of_birth}.' 29 ) 30 assert reality == my_expectation 31 32 33def assert_person_factory_works( 34 first_name, last_name, 35 sex, year_of_birth, 36 ):I remove the say_hello function from
tests/test_person.py1import src.person 2 3 4def assert_say_hello_works( 5 first_name, last_name, 6 year_of_birth, 7 ):all the tests are still green.
I add a git commit message in the other terminal
git commit -am \ 'move say_hello function to src'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 tests/test_person.py.
RED: make it fail
I close
tests/test_person.pyI delete all the text in
src/person/__init__.pyand the terminal shows AttributeErrorAttributeError: module 'src.person' has no attribute 'person'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
src/person/__init__.py1personthe terminal is my friend, and shows NameError
NameError: name 'person' is not definedI point
personto None (the simplest object) to define it1# person 2person = Nonethe terminal is my friend, and shows TypeError
TypeError: 'NoneType' object is not callablebecause
personpoints to None and I cannot call None like a function.I change
personto a function to make it callable1# person 2# person = None 3def person(): 4 return Nonethe terminal is my friend, and shows TypeError
TypeError: person() got an unexpected keyword argument 'first_name'because this function definition does not allow any inputs, the parentheses are empty.
I add
first_namein the parentheses so the function can take input1# person 2# person = None 3# def person(): 4def person(first_name): 5 return Nonethe terminal is my friend, and shows TypeError
TypeError: person() 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_nameto the parentheses1# person 2# person = None 3# def person(): 4# def person(first_name): 5def person(first_name, last_name): 6 return Nonethe terminal is my friend, and shows TypeError
TypeError: person() got an unexpected keyword argument 'sex'because the function definition only allows two inputs (
first_nameandlast_name) and it was called with a keyword argument (sex).I add
sexto the parentheses1# person 2# person = None 3# def person(): 4# def person(first_name): 5# def person(first_name, last_name): 6def person(first_name, last_name, sex): 7 return Nonethe terminal is my friend, and shows TypeError
TypeError: person() 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_birthto the parentheses1# person 2# person = None 3# def person(): 4# def person(first_name): 5# def person(first_name, last_name): 6# def person(first_name, last_name, sex): 7def person( 8 first_name, last_name, 9 sex, year_of_birth, 10 ): 11 return Nonethe 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# person 2# person = None 3# def person(): 4# def person(first_name): 5# def person(first_name, last_name): 6# def person(first_name, last_name, sex): 7def person( 8 first_name, last_name, 9 sex, year_of_birth, 10 ): 11 # return None 12 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# person 5# person = None 6# def person(): 7# def person(first_name): 8# def person(first_name, last_name): 9# def person(first_name, last_name, sex): 10def person( 11 first_name, last_name, 12 sex, year_of_birth, 13 ): 14 # return None 15 return 'mary, public, F, 2000'the terminal is my friend, and shows NameError
NameError: name 'say_hello' is not definedI point it to None to define it
1# say_hello 2say_hello = None 3 4 5# personthe terminal is my friend, and shows TypeError
TypeError: 'NoneType' object is not callablebecause
say_hellopoints to None and I cannot call None like a function.I make
say_helloa function to make it callable1# say_hello 2# say_hello = None 3def say_hello(): 4 return None 5 6 7# personthe 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_namein the parentheses1# say_hello 2# say_hello = None 3# def say_hello(): 4def say_hello(first_name): 5 return None 6 7 8# personthe 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_nameto the parentheses1# 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# personthe 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_nameandlast_name) and it was called with a keyword argument (year_of_birth).I add
year_of_birthto the parentheses1# 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# personthe 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# personthe terminal is my friend, and shows AssertionError
AssertionError: assert 'mary, public, F, 2000' == 'john, smith, M, 1580'because the person function always returns
'mary, public, F, 2000'and this test expects'john, smith, M, 1580'.I change the return statement of the person function to see the difference between the input and the expected output (remember the identity function?)
13# person 14# person = None 15# def person(): 16# def person(first_name): 17# def person(first_name, last_name): 18# def person(first_name, last_name, sex): 19def person( 20 first_name, last_name, 21 sex, year_of_birth, 22 ): 23 # return None 24 # return 'mary, public, F, 2000' 25 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,sexandyear_of_birthare part of the output.I change the return statement to an f-string with those values
13# person 14# person = None 15# def person(): 16# def person(first_name): 17# def person(first_name, last_name): 18# def person(first_name, last_name, sex): 19def person( 20 first_name, last_name, 21 sex, year_of_birth, 22 ): 23 # return None 24 # return 'mary, public, F, 2000' 25 # return first_name, last_name, sex, year_of_birth, 26 return ( 27 f'{first_name}, {last_name},' 28 f' {sex}, {year_of_birth}' 29 )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# personthe terminal is my friend, and shows AssertionError
AssertionError: assert ('mary', 'public', 'F', 2000) == 'mary, public, F, 2000'the
first_nameandlast_nameare 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# personthe 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
26is the age.I change the
year_of_birthvalue to a calculation of the age1# 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# personall the tests are green!
I remove the commented lines from
src/person/__init__.py1def 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 person( 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
src/person/__init__.pyI 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
personcd ..the terminal shows
...\pumping_pythonI am back in the
pumping_pythondirectory.
review
I ran tests to write one function that makes a string for a person when given
first_name,last_name,sexandyear_of_birthso I do not have to make one function for each person.I also ran tests to make another function that makes an f-string to represent the person saying hello when I give it
first_name,last_name, andyear_of_birth.My tests and solutions have a few problems,
Each test is the same two tests, there has to be a way that I can use one test for all the people.
The person factory and say_hello functions use three of the same inputs
first_namelast_nameyear_of_birth
There has to be a better way, where I can give those values once, and get a representation for a person when I call the person 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
what is next?
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.