how to make a person
This is an exercise in making dictionaries with functions, two important objects to know in Python.
I want to make a contact list or database of people. I can use a function to represent filling out information for a person, for example
First Name
Last Name (Surname)
Sex
Year of Birth
preview
I have these tests by the end of the chapter
1import datetime
2import src.person
3import unittest
4import random
5
6
7def pick_one(*choices):
8 return random.choice(choices)
9
10
11def get_random_name():
12 return pick_one(
13 'jane', 'joe', 'john', 'person',
14 'doe', 'smith', 'blow', 'public',
15 )
16
17
18def get_current_year():
19 return datetime.datetime.now().year
20
21
22def calculate_age(year_of_birth):
23 return (
24 get_current_year()
25 - year_of_birth
26 )
27
28
29def get_random_year_of_birth():
30 this_year = get_current_year()
31 return random.randint(
32 this_year-120, this_year
33 )
34
35
36class TestPerson(unittest.TestCase):
37
38 def test_factory_w_keyword_arguments(self):
39 a_person = dict(
40 first_name=get_random_name(),
41 last_name=get_random_name(),
42 sex=pick_one('F', 'M'),
43 )
44 year_of_birth = get_random_year_of_birth()
45
46 reality = src.person.factory(
47 **a_person,
48 year_of_birth=year_of_birth,
49 )
50 my_expectation = dict(
51 a_person,
52 age=calculate_age(year_of_birth),
53 )
54 self.assertEqual(reality, my_expectation)
55
56 def test_factory_w_optional_arguments(self):
57 first_name = get_random_name()
58 year_of_birth = get_random_year_of_birth()
59
60 reality = src.person.factory(
61 first_name=first_name,
62 year_of_birth=year_of_birth,
63 )
64 my_expectation = dict(
65 first_name=first_name,
66 last_name='doe',
67 sex='M',
68 age=calculate_age(year_of_birth),
69 )
70 self.assertEqual(reality, my_expectation)
71
72 def test_factory_person_says_hello(self):
73 first_name = 'joe'
74 last_name = 'blow'
75 year_of_birth = get_random_year_of_birth()
76 age = calculate_age(year_of_birth)
77
78 joe = src.person.factory(
79 first_name=first_name,
80 last_name=last_name,
81 year_of_birth=year_of_birth,
82 )
83
84 reality = src.person.say_hello(joe)
85 my_expectation = (
86 f'Hello, my name is {first_name}'
87 f' {last_name} and I am {age}'
88 )
89 self.assertEqual(reality, my_expectation)
90
91 first_name = 'jane'
92 year_of_birth = get_random_year_of_birth()
93 age = calculate_age(year_of_birth)
94
95 jane = src.person.factory(
96 first_name=first_name,
97 sex='F',
98 year_of_birth=year_of_birth
99 )
100
101 reality = src.person.say_hello(jane)
102 my_expectation = (
103 f'Hello, my name is {first_name}'
104 f' doe and I am {age}'
105 )
106 self.assertEqual(reality, my_expectation)
107
108 first_name = 'john'
109 last_name = 'smith'
110 year_of_birth = get_random_year_of_birth()
111 age = calculate_age(year_of_birth)
112
113 john = src.person.factory(
114 first_name=first_name,
115 last_name=last_name,
116 year_of_birth=year_of_birth,
117 )
118
119 reality = src.person.say_hello(john)
120 my_expectation = (
121 f'Hello, my name is {first_name}'
122 f' {last_name} and I am {age}'
123 )
124 self.assertEqual(reality, my_expectation)
125
126 first_name = 'mary'
127 last_name = 'public'
128 year_of_birth = get_random_year_of_birth()
129 age = calculate_age(year_of_birth)
130
131 mary = src.person.factory(
132 first_name=first_name,
133 last_name=last_name,
134 year_of_birth=year_of_birth,
135 sex='F',
136 )
137
138 reality = src.person.say_hello(mary)
139 my_expectation = (
140 f'Hello, my name is {first_name}'
141 f' {last_name} and I am {age}'
142 )
143 self.assertEqual(reality, my_expectation)
144
145
146# Exceptions seen
147# AssertionError
148# NameError
149# AttributeError
150# TypeError
151# SyntaxError
start the project
I name this project
personI open a terminal
I use uv to make a directory for the project and initialize it
uv init personthe terminal shows
Initialized project `person` at `.../pumping_python/person`then goes back to the command line.
I change directory to the project
cd personthe terminal shows I am in the
personfolder.../pumping_python/personI use mkdir to make a folder named
srcmkdir srcthe terminal goes back to the command line.
I use the mv program to change the name of
main.pytoperson.pyand move it to thesrcfoldermv main.py src/person.pyMove-Item main.py src/person.pythe terminal goes back to the command line.
I make a directory for the tests
mkdir teststhe terminal goes back to the command line.
I make the
testsdirectory a Python packageDanger
use 2 underscores (__) before and after
initfor__init__.pynot_init_.pytouch tests/__init__.pyNew-Item tests/__init__.pythe terminal goes back to the command line.
I make a Python file for the tests in the
testsdirectorytouch tests/test_person.pyNew-Item tests/test_person.pythe terminal goes back to the command line.
I open
test_person.pyI add the first failing test to
test_person.py1import unittest 2 3 4class TestPerson(unittest.TestCase): 5 6 def test_failure(self): 7 self.assertFalse(True)I go back to the terminal to make a requirements file for the Python packages I need
echo "pytest" > requirements.txtthe terminal goes back to the command line.
I add pytest-watcher to the requirements file
echo "pytest-watcher" >> requirements.txtthe terminal goes back to the command line.
I use uv to install pytest-watcher with the requirements file
uv add --requirement requirements.txtthe terminal shows that it installed pytest-watcher and its dependencies.
I add the new files and folders to git for tracking
git add .the terminal goes back to the command line.
I add a git commit message
git commit -am 'setup project'the terminal shows
[main (root-commit) a0b12c3] setup project 9 files changed, 145 insertions(+) create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 README.md create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 src/person.py create mode 100644 tests/__init__.py create mode 100644 tests/test_person.py create mode 100644 uv.lockthen goes back to the command line.
I use pytest-watcher to run the tests automatically
uv run pytest-watcher . --nowthe terminal is my friend, and shows AssertionError
======================== FAILURES ======================== ______________________ TestPerson.test_failure ________________________ self = <tests.test_person.TestPerson testMethod=test_failure> def test_failure(self): > self.assertFalse(True) E AssertionError: True is not false tests/test_person.py:7: AssertionError ================ short test summary info ================= FAILED tests/test_person.py::TestPerson::test_failure - AssertionError: True is not false =================== 1 failed in X.YZs ====================if the terminal does not show the same error, then check if
your
tests/__init__.pyhas two underscores (__) before and afterinitfor__init__.pynot_init_.pyyou ran
echo "pytest-watcher" >> requirements.txt, to addpytest-watcherto the requirements file
and try
uv run pytest-watcher . --nowagainI add AssertionError to the list of Exceptions seen, in
test_person.py4class TestPerson(unittest.TestCase): 5 6 def test_failure(self): 7 self.assertFalse(True) 8 9 10# Exceptions seen 11# AssertionErrorI change True to False in the assertion
7 self.assertFalse(False)the test passes.
test_factory_w_keyword_arguments
RED: make it fail
I change test_failure to test_factory_w_keyword_arguments
4class TestPerson(unittest.TestCase): 5 6 def test_factory_w_keyword_arguments(self): 7 reality = src.person.factory() 8 my_expectation = None 9 self.assertEqual(reality, my_expectation) 10 11 12# Exceptions seen 13# AssertionErrorthe terminal is my friend, and shows NameError
NameError: name 'src' is not definedbecause there is no definition for
srcintest_person.pyI add NameError to the list of Exceptions seen
12# Exceptions seen 13# AssertionError 14# NameError
GREEN: make it pass
I add an import statement for the
personmodule at the top oftest_person.py1import src.person 2import unittestimport src.personbrings in an object (everything in Python is an object) for theperson.pymodule from thesrcfolder so I can use it intest_person.pythe terminal is my friend, and shows AttributeError
AttributeError: module 'src.person' has no attribute 'factory'because there is nothing in
person.pynamedfactory
I add AttributeError to the list of Exceptions seen
13# Exceptions seen 14# AssertionError 15# NameError 16# AttributeErrorI open
person.pyfrom thesrcfolderI delete all the text in the file, then add a function to
person.py1def factory(): 2 return Nonethe test passes because when
src.person.factory()is called, Python checksperson.pyin thesrcfolder for a function definition with the namefactoryand finds it.
REFACTOR: make it better
I want the function to take an argument called
first_name. I add it to the call from the test intest_person.py7 def test_factory_w_keyword_arguments(self): 8 # reality = src.person.factory() 9 reality = src.person.factory( 10 first_name='first_name', 11 ) 12 my_expectation = None 13 self.assertEqual(reality, my_expectation) 14 15 16# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: factory() got an unexpected keyword argument 'first_name'because the call uses a name (
first_name) that is not in the parentheses of its definition.I add TypeError to the list of Exceptions seen
16# Exceptions seen 17# AssertionError 18# NameError 19# AttributeError 20# TypeErrorI add
first_nameas an input parameter to the function inperson.py1# def factory(): 2def factory(first_name): 3 return Nonethe test passes.
I want the function to take an argument called
last_name. I add it to the test intest_person.py7 def test_factory_w_keyword_arguments(self): 8 # reality = src.person.factory() 9 reality = src.person.factory( 10 first_name='first_name', 11 last_name='last_name', 12 ) 13 my_expectation = None 14 self.assertEqual(reality, my_expectation) 15 16 17# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: factory() got an unexpected keyword argument 'last_name'. Did you mean 'first_name'?because the test called the factory function with a name (
last_name) that is not in the function definition.I add
last_nameto the function definition inperson.py1# def factory(): 2# def factory(first_name): 3def factory(first_name, last_name): 4 return Nonethe test passes.
I want the function to take an argument called
sex. I add it to the test intest_person.py7 def test_factory_w_keyword_arguments(self): 8 # reality = src.person.factory() 9 reality = src.person.factory( 10 first_name='first_name', 11 last_name='last_name', 12 sex='M', 13 ) 14 my_expectation = None 15 self.assertEqual(reality, my_expectation) 16 17 18# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: factory() got an unexpected keyword argument 'sex'because the test called the factory function with a name (
sex) that is not in the function definition.I add
sexas an input parameter to the factory function inperson.py1# def factory(): 2# def factory(first_name): 3# def factory(first_name, last_name): 4def factory( 5 first_name, last_name, 6 sex, 7 ): 8 return Nonethe test passes.
I want the function to take an argument for
year_of_birth. I add it to the test intest_person.py7 def test_factory_w_keyword_arguments(self): 8 # reality = src.person.factory() 9 reality = src.person.factory( 10 first_name='first_name', 11 last_name='last_name', 12 sex='M', 13 year_of_birth=2000, 14 ) 15 my_expectation = None 16 self.assertEqual(reality, my_expectation) 17 18 19# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: factory() got an unexpected keyword argument 'year_of_birth'because the test called the factory function with a name (
year_of_birth) that is not in the function definition.I add the name to the function definition in
person.py1# def factory(): 2# def factory(first_name): 3# def factory(first_name, last_name): 4def factory( 5 first_name, last_name, 6 sex, year_of_birth, 7 ): 8 return Nonethe test passes.
I want the factory function to return a dictionary (any key-value pairs in curly braces
{ }separated by commas) as output when it is called. I changemy_expectationintest_person.py7 def test_factory_w_keyword_arguments(self): 8 # reality = src.person.factory() 9 reality = src.person.factory( 10 first_name='first_name', 11 last_name='last_name', 12 sex='M', 13 year_of_birth=2000, 14 ) 15 # my_expectation = None 16 my_expectation = dict() 17 self.assertEqual(reality, my_expectation) 18 19 20# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: None != {}because this happens when the test runs
reality = src.person.factory( first_name='first_name', last_name='last_name', sex='M', year_of_birth=2000, ) # the factory function returns Nonemy_expectation = dict() # the dict constructor returns {}self.assertEqual(reality, my_expectation) self.assertEqual(None , {} )which raises AssertionError since the function returns None and the assertion expects
{}.I change None in the return statement, to give the test what it wants, in
person.py1# def factory(): 2# def factory(first_name): 3# def factory(first_name, last_name): 4def factory( 5 first_name, last_name, 6 sex, year_of_birth, 7 ): 8 # return None 9 return {}the test passes because
{}anddict()are two ways to make the empty dictionary.
I add a key called
first_nameto the dictionary formy_expectation, with the same value as what is given in the call to the factory function intest_person.py7 def test_factory_w_keyword_arguments(self): 8 # reality = src.person.factory() 9 reality = src.person.factory( 10 first_name='first_name', 11 last_name='last_name', 12 sex='M', 13 year_of_birth=2000, 14 ) 15 # my_expectation = None 16 # my_expectation = dict() 17 my_expectation = dict( 18 first_name='first_name', 19 ) 20 self.assertEqual(reality, my_expectation) 21 22 23# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {} != {'first_name': 'first_name'}because this happens when the test runs
reality = src.person.factory( first_name='first_name', last_name='last_name', sex='M', year_of_birth=2000, ) {} # the factory function returns {}my_expectation = dict(first_name='first_name') {'first_name': 'first_name'}self.assertEqual(reality, my_expectation ) # is the same as self.assertEqual({} , {'first_name': 'first_name'})which raises AssertionError since the function returns the empty dictionary and the assertion expects one with
first_nameas the key.I change the return statement to give the test what it wants, in
person.py1# def factory(): 2# def factory(first_name): 3# def factory(first_name, last_name): 4def factory( 5 first_name, last_name, 6 sex, year_of_birth, 7 ): 8 # return None 9 # return {} 10 return {'first_name': 'first_name'}the test passes.
I change the value of
first_nameto'jane'to use a real name forrealityandmy_expectation, intest_person.py7 def test_factory_w_keyword_arguments(self): 8 # reality = src.person.factory() 9 reality = src.person.factory( 10 # first_name='first_name', 11 first_name='jane', 12 last_name='last_name', 13 sex='M', 14 year_of_birth=2000, 15 ) 16 # my_expectation = None 17 # my_expectation = dict() 18 my_expectation = dict( 19 # first_name='first_name', 20 first_name='jane', 21 ) 22 self.assertEqual(reality, my_expectation) 23 24 25# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': 'first_name'} != {'first_name': 'jane'}because this happens when the test runs
reality = src.person.factory( first_name='jane', last_name='last_name', sex='M', year_of_birth=2000, ) # the factory function returns {'first_name': 'first_name'}my_expectation = dict(first_name='jane') # the dict constructor returns {'first_name': 'jane'}self.assertEqual(reality, my_expectation) # is the same as self.assertEqual( {'first_name': 'first_name'}, {'first_name': 'jane'} )which raises AssertionError since I changed the value for
first_nameto'jane'inmy_expectationand the function returns a dictionary with a different value ('first_name').I change the value for the
first_namekey inperson.py1# def factory(): 2# def factory(first_name): 3# def factory(first_name, last_name): 4def factory( 5 first_name, last_name, 6 sex, year_of_birth, 7 ): 8 # return None 9 # return {} 10 # return {'first_name': 'first_name'} 11 return {'first_name': 'jane'}the test passes.
I typed the value for
first_nametwo times in the test, which means I had to make a change in two places when I wanted a different value for it. I add a variable for'jane'intest_person.py7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 10 # reality = src.person.factory()I use the variable to remove repetition of
'jane'from the test7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 10 # reality = src.person.factory() 11 reality = src.person.factory( 12 # first_name='first_name', 13 # first_name='jane', 14 first_name=first_name, 15 last_name='last_name', 16 sex='M', 17 year_of_birth=2000, 18 ) 19 # my_expectation = None 20 # my_expectation = dict() 21 my_expectation = dict( 22 # first_name='first_name', 23 # first_name='jane', 24 first_name=first_name, 25 ) 26 self.assertEqual(reality, my_expectation) 27 28 29# Exceptions seenthe test is still green. I now only need to change the value of
first_namein one place in the test.
I add a key called
last_nameto the dictionary formy_expectation, with the same value as what is given in the call to the factory function intest_person.py7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 10 # reality = src.person.factory() 11 reality = src.person.factory( 12 # first_name='first_name', 13 # first_name='jane', 14 first_name=first_name, 15 last_name='last_name', 16 sex='M', 17 year_of_birth=2000, 18 ) 19 # my_expectation = None 20 # my_expectation = dict() 21 my_expectation = dict( 22 # first_name='first_name', 23 # first_name='jane', 24 first_name=first_name, 25 last_name='last_name', 26 ) 27 self.assertEqual(reality, my_expectation) 28 29 30# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': 'jane'} != {'first_name': 'jane', 'last_name': 'last_name'}because this happens when the test runs
first_name = 'jane'reality = src.person.factory( first_name=first_name, last_name='last_name', sex='M', year_of_birth=2000, ) # the factory function returns {'first_name': 'jane'}my_expectation = dict( first_name=first_name, last_name='last_name', ) # the dict constructor returns { 'first_name': 'jane', 'last_name': 'last_name' }self.assertEqual(reality, my_expectation) # is the same as self.assertEqual( {'first_name': 'jane'}, {'first_name': 'jane', 'last_name': 'last_name'} )I change the return statement in
person.py1# def factory(): 2# def factory(first_name): 3# def factory(first_name, last_name): 4def factory( 5 first_name, last_name, 6 sex, year_of_birth, 7 ): 8 # return None 9 # return {} 10 # return {'first_name': 'first_name'} 11 # return {'first_name': 'jane'} 12 return { 13 'first_name': 'jane', 14 'last_name': 'last_name', 15 }the test passes.
I change the value of
last_nameto'doe'to use a real name forrealityandmy_expectation, intest_person.py7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 10 # reality = src.person.factory() 11 reality = src.person.factory( 12 # first_name='first_name', 13 # first_name='jane', 14 first_name=first_name, 15 # last_name='last_name', 16 last_name='doe', 17 sex='M', 18 year_of_birth=2000, 19 ) 20 # my_expectation = None 21 # my_expectation = dict() 22 my_expectation = dict( 23 # first_name='first_name', 24 # first_name='jane', 25 first_name=first_name, 26 # last_name='last_name', 27 last_name='doe', 28 ) 29 self.assertEqual(reality, my_expectation) 30 31 32# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': 'jane', 'last_name': 'last_name'} != {'first_name': 'jane', 'last_name': 'doe'}because this happens when the test runs
first_name = 'jane'reality = src.person.factory( first_name=first_name, last_name='doe', sex='M', year_of_birth=2000, ) # the factory function returns { 'first_name': 'jane', 'last_name': 'last_name' }my_expectation = dict( first_name=first_name, last_name='doe', ) # the dict constructor returns { 'first_name': 'jane', 'last_name': 'doe' }self.assertEqual(reality, my_expectation) # is the same as self.assertEqual( {'first_name': 'jane', 'last_name': 'last_name'}, {'first_name': 'jane', 'last_name': 'doe'} )which raises AssertionError since I changed the value for
last_nameto'doe'inmy_expectationand the function returns a dictionary with a different value ('last_name').I change the value for the
last_namekey inperson.py1# def factory(): 2# def factory(first_name): 3# def factory(first_name, last_name): 4def factory( 5 first_name, last_name, 6 sex, year_of_birth, 7 ): 8 # return None 9 # return {} 10 # return {'first_name': 'first_name'} 11 # return {'first_name': 'jane'} 12 return { 13 'first_name': 'jane', 14 # 'last_name': 'last_name', 15 'last_name': 'doe', 16 }the test passes.
I typed the value for
last_nametwo times in the test, which means I had to make a change in two places when I wanted a different value for it. I add a variable for'doe'intest_person.py7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 last_name = 'doe' 10 11 # reality = src.person.factory()I use the variable to remove repetition of
'doe'from the test7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 last_name = 'doe' 10 11 # reality = src.person.factory() 12 reality = src.person.factory( 13 # first_name='first_name', 14 # first_name='jane', 15 first_name=first_name, 16 # last_name='last_name', 17 # last_name='doe', 18 last_name=last_name, 19 sex='M', 20 year_of_birth=2000, 21 ) 22 # my_expectation = None 23 # my_expectation = dict() 24 my_expectation = dict( 25 # first_name='first_name', 26 # first_name='jane', 27 first_name=first_name, 28 # last_name='last_name', 29 # last_name='doe', 30 last_name=last_name, 31 ) 32 self.assertEqual(reality, my_expectation) 33 34 35# Exceptions seenthe test is still green. I now only need to change the value of
last_namein one place in the test.
I add a key called
sexto the dictionary formy_expectation, with the same value as what is given in the call to the factory function intest_person.py7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 last_name = 'doe' 10 11 # reality = src.person.factory() 12 reality = src.person.factory( 13 # first_name='first_name', 14 # first_name='jane', 15 first_name=first_name, 16 # last_name='last_name', 17 # last_name='doe', 18 last_name=last_name, 19 sex='M', 20 year_of_birth=2000, 21 ) 22 # my_expectation = None 23 # my_expectation = dict() 24 my_expectation = dict( 25 # first_name='first_name', 26 # first_name='jane', 27 first_name=first_name, 28 # last_name='last_name', 29 # last_name='doe', 30 last_name=last_name, 31 sex='M', 32 ) 33 self.assertEqual(reality, my_expectation) 34 35 36# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': 'jane', 'last_name': 'doe'} != {'first_name': 'jane', 'last_name': 'doe', 'sex': 'M'}because this happens when the test runs
first_name = 'jane' last_name = 'doe'reality = src.person.factory( first_name=first_name, last_name=last_name, sex='M', year_of_birth=2000, ) # the factory function returns { 'first_name': 'jane', 'last_name': 'doe' }my_expectation = dict( first_name=first_name, last_name=last_name, sex='M' ) # the dict constructor returns { 'first_name': 'jane', 'last_name': 'doe', 'sex': 'M' }self.assertEqual(reality, my_expectation) # is the same as self.assertEqual( {'first_name': 'jane', 'last_name': 'doe'}, {'first_name': 'jane', 'last_name': 'doe', 'sex': 'M'} )I add a new key to the return statement in
person.py1# def factory(): 2# def factory(first_name): 3# def factory(first_name, last_name): 4def factory( 5 first_name, last_name, 6 sex, year_of_birth, 7 ): 8 # return None 9 # return {} 10 # return {'first_name': 'first_name'} 11 # return {'first_name': 'jane'} 12 return { 13 'first_name': 'jane', 14 # 'last_name': 'last_name', 15 'last_name': 'doe', 16 'sex': 'M', 17 }the test passes.
I change the value of
sexto'F'forrealityandmy_expectation, intest_person.py7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 last_name = 'doe' 10 11 # reality = src.person.factory() 12 reality = src.person.factory( 13 # first_name='first_name', 14 # first_name='jane', 15 first_name=first_name, 16 # last_name='last_name', 17 # last_name='doe', 18 last_name=last_name, 19 # sex='M', 20 sex='F', 21 year_of_birth=2000, 22 ) 23 # my_expectation = None 24 # my_expectation = dict() 25 my_expectation = dict( 26 # first_name='first_name', 27 # first_name='jane', 28 first_name=first_name, 29 # last_name='last_name', 30 # last_name='doe', 31 last_name=last_name, 32 # sex='M', 33 sex='F', 34 ) 35 self.assertEqual(reality, my_expectation) 36 37 38# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': 'jane', 'last_name': 'doe', 'sex': 'M'} != {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F'}because this happens when the test runs
first_name = 'jane' last_name = 'doe'reality = src.person.factory( first_name=first_name, last_name=last_name, sex='F', year_of_birth=2000, ) # the factory function returns { 'first_name': 'jane', 'last_name': 'doe', 'sex': 'M' }my_expectation = dict( first_name=first_name, last_name=last_name, sex='F' ) # the dict constructor returns { 'first_name': 'jane', 'last_name': 'doe', 'sex': 'F' }self.assertEqual(reality, my_expectation) # is the same as self.assertEqual( {'first_name': 'jane', 'last_name': 'doe', 'sex': 'M'}, {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F'} )which raises AssertionError since I changed the value for
sexto'F'inmy_expectationand the function returns a dictionary with a different value ('M').I change the value for the
sexkey inperson.py1# def factory(): 2# def factory(first_name): 3# def factory(first_name, last_name): 4def factory( 5 first_name, last_name, 6 sex, year_of_birth, 7 ): 8 # return None 9 # return {} 10 # return {'first_name': 'first_name'} 11 # return {'first_name': 'jane'} 12 return { 13 'first_name': 'jane', 14 # 'last_name': 'last_name', 15 'last_name': 'doe', 16 # 'sex': 'M', 17 'sex': 'F', 18 }the test passes.
I typed the value for
sextwo times in the test, which means I had to make a change in two places when I wanted a different value for it. I add a variable for'F'intest_person.py7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 last_name = 'doe' 10 sex = 'F' 11 12 # reality = src.person.factory()I use the variable to remove repetition of
'F'7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 last_name = 'doe' 10 sex = 'F' 11 12 # reality = src.person.factory() 13 reality = src.person.factory( 14 # first_name='first_name', 15 # first_name='jane', 16 first_name=first_name, 17 # last_name='last_name', 18 # last_name='doe', 19 last_name=last_name, 20 # sex='M', 21 # sex='F', 22 sex=sex, 23 year_of_birth=2000, 24 ) 25 # my_expectation = None 26 # my_expectation = dict() 27 my_expectation = dict( 28 # first_name='first_name', 29 # first_name='jane', 30 first_name=first_name, 31 # last_name='last_name', 32 # last_name='doe', 33 last_name=last_name, 34 # sex='M', 35 # sex='F', 36 sex=sex, 37 ) 38 self.assertEqual(reality, my_expectation) 39 40 41# Exceptions seenthe test is still green. I now only need to change the value of
sexin one place in the test.
I want the factory function to return the age of the person it makes. I add a key to
my_expectation7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 last_name = 'doe' 10 sex = 'F' 11 12 # reality = src.person.factory() 13 reality = src.person.factory( 14 # first_name='first_name', 15 # first_name='jane', 16 first_name=first_name, 17 # last_name='last_name', 18 # last_name='doe', 19 last_name=last_name, 20 # sex='M', 21 # sex='F', 22 sex=sex, 23 year_of_birth=2000, 24 ) 25 # my_expectation = None 26 # my_expectation = dict() 27 my_expectation = dict( 28 # first_name='first_name', 29 # first_name='jane', 30 first_name=first_name, 31 # last_name='last_name', 32 # last_name='doe', 33 last_name=last_name, 34 # sex='M', 35 # sex='F', 36 sex=sex, 37 age=2026-2000, 38 ) 39 self.assertEqual(reality, my_expectation) 40 41 42# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F'} != {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 26}because this happens when the test runs
first_name = 'jane' last_name = 'doe' sex = 'F'reality = src.person.factory( first_name=first_name, last_name=last_name, sex=sex, year_of_birth=2000, ) # the factory function returns { 'first_name': 'jane', 'last_name': 'last_name', 'sex': 'F' }my_expectation = dict( first_name=first_name, last_name=last_name, sex=sex, age=2026-2000, ) # the dict constructor returns { 'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 26 }self.assertEqual(reality, my_expectation) self.assertEqual( {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F'}, {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 26} )I add a new key to the return statement in
person.py1# def factory(): 2# def factory(first_name): 3# def factory(first_name, last_name): 4def factory( 5 first_name, last_name, 6 sex, year_of_birth, 7 ): 8 # return None 9 # return {} 10 # return {'first_name': 'first_name'} 11 # return {'first_name': 'jane'} 12 return { 13 'first_name': 'jane', 14 # 'last_name': 'last_name', 15 'last_name': 'doe', 16 # 'sex': 'M', 17 'sex': 'F', 18 'age': 26, 19 }the test passes.
I change
2000to1996forrealityandmy_expectation, intest_person.py7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 last_name = 'doe' 10 sex = 'F' 11 12 # reality = src.person.factory() 13 reality = src.person.factory( 14 # first_name='first_name', 15 # first_name='jane', 16 first_name=first_name, 17 # last_name='last_name', 18 # last_name='doe', 19 last_name=last_name, 20 # sex='M', 21 # sex='F', 22 sex=sex, 23 # year_of_birth=2000, 24 year_of_birth=1996, 25 ) 26 # my_expectation = None 27 # my_expectation = dict() 28 my_expectation = dict( 29 # first_name='first_name', 30 # first_name='jane', 31 first_name=first_name, 32 # last_name='last_name', 33 # last_name='doe', 34 last_name=last_name, 35 # sex='M', 36 # sex='F', 37 sex=sex, 38 # age=2026-2000, 39 age=2026-1996, 40 ) 41 self.assertEqual(reality, my_expectation) 42 43 44# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 26} != {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 30}because this happens when the test runs
first_name = 'jane' last_name = 'doe' sex = 'F'reality = src.person.factory( first_name=first_name, last_name=last_name, sex=sex, year_of_birth=1996, ) # the factory function returns { 'first_name': 'jane', 'last_name': 'doe', 'sex': 'F' 'age': 26 }my_expectation = dict( first_name=first_name, last_name=last_name, sex=sex, age=2026-1996 ) # the dict constructor returns { 'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 30 }self.assertEqual(reality, my_expectation) self.assertEqual( {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 26}, {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 30} )which raises AssertionError since I changed the calculation for
ageto2026-1996inmy_expectationand the function returns a dictionary with a different value (26).I change the value for the
agekey inperson.py1# def factory(): 2# def factory(first_name): 3# def factory(first_name, last_name): 4def factory( 5 first_name, last_name, 6 sex, year_of_birth, 7 ): 8 # return None 9 # return {} 10 # return {'first_name': 'first_name'} 11 # return {'first_name': 'jane'} 12 return { 13 'first_name': 'jane', 14 # 'last_name': 'last_name', 15 'last_name': 'doe', 16 # 'sex': 'M', 17 'sex': 'F', 18 # 'age': 26, 19 'age': 30, 20 }the test passes.
I remove the commented lines
1def factory( 2 first_name, last_name, 3 sex, year_of_birth, 4 ): 5 return { 6 'first_name': 'jane', 7 'last_name': 'doe', 8 'sex': 'F', 9 'age': 30, 10 }I typed the year of birth two times in the test, which means I had to make a change in two places when I wanted a different value for it. I add a variable for
1996intest_person.py7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 last_name = 'doe' 10 sex = 'F' 11 year_of_birth = 1996 12 13 # reality = src.person.factory()I use the variable to remove repetition of
1996from the test7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 last_name = 'doe' 10 sex = 'F' 11 year_of_birth = 1996 12 13 # reality = src.person.factory() 14 reality = src.person.factory( 15 # first_name='first_name', 16 # first_name='jane', 17 first_name=first_name, 18 # last_name='last_name', 19 # last_name='doe', 20 last_name=last_name, 21 # sex='M', 22 # sex='F', 23 sex=sex, 24 # year_of_birth=2000, 25 # year_of_birth=1996, 26 year_of_birth=year_of_birth, 27 ) 28 # my_expectation = None 29 # my_expectation = dict() 30 my_expectation = dict( 31 # first_name='first_name', 32 # first_name='jane', 33 first_name=first_name, 34 # last_name='last_name', 35 # last_name='doe', 36 last_name=last_name, 37 # sex='M', 38 # sex='F', 39 sex=sex, 40 # age=2026-2000, 41 # age=2026-1996, 42 age=2026-year_of_birth, 43 ) 44 self.assertEqual(reality, my_expectation) 45 46 47# Exceptions seenthe test is still green. I now only need to change the value of
year_of_birthin one place in the test.I remove the commented lines
7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 last_name = 'doe' 10 sex = 'F' 11 year_of_birth = 1996 12 13 reality = src.person.factory( 14 first_name=first_name, 15 last_name=last_name, 16 sex=sex, 17 year_of_birth=year_of_birth, 18 ) 19 my_expectation = dict( 20 first_name=first_name, 21 last_name=last_name, 22 sex=sex, 23 age=2026-year_of_birth, 24 ) 25 self.assertEqual(reality, my_expectation) 26 27 28# Exceptions seenI open a new terminal then change directories to
personcd personI add a git commit message in the other terminal
git commit -am 'add test_factory_w_keyword_arguments'the terminal shows a summary of the changes then goes back to the command line.
test factory with current year
There is a problem with the calculation for the age, it will be wrong if this program is run after 2026.
I want the value of the age to be a calculation based on the current year so that it will always be correct (at least most of the time).
I can do that with the `datetime module`_ from The Python Standard Library which is used for dates and times.
RED: make it fail
I go back to the terminal where the tests are running
I change
2026inmy_expectationto use a method from the `datetime module`_7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 last_name = 'doe' 10 sex = 'F' 11 year_of_birth = 1996 12 13 reality = src.person.factory( 14 first_name=first_name, 15 last_name=last_name, 16 sex=sex, 17 year_of_birth=year_of_birth, 18 ) 19 my_expectation = dict( 20 first_name=first_name, 21 last_name=last_name, 22 sex=sex, 23 # age=2026-year_of_birth, 24 age=( 25 datetime.datetime.today().year 26 - year_of_birth 27 ), 28 ) 29 self.assertEqual(reality, my_expectation) 30 31 32# Exceptions seenthe terminal is my friend, and shows NameError
NameError: name 'datetime' is not defined. Did you forget to import 'datetime'?
GREEN: make it pass
I add an import statement for the `datetime module`_ at the top of
test_person.py1import datetime 2import src.person 3import unittestthe test passes.
I add a git commit message in the other terminal
git commit -am \ 'use current year for age calculation'the terminal shows a summary of the changes then goes back to the command line.
import datetimebrings in an object (everything in Python is an object) for the `datetime module`_ so I can use it intest_person.py. This means I can assume there is adatetime.pyfile on the computer that came with Pythonimport datetime # import object for datetime.pydatetime.datetimeis a reference to the `datetime object`_ of the `datetime module`_. Wait a minute, that is the same name again. Do I have to remember all this?datetime.datetime # use datetime object from datetime.pydatetime.datetime.now()is a call to the `now method`_ of the `datetime.datetime object`_ from the `datetime module`_, it returns a `datetime.datetime object`_. Oh boy!datetime.datetime.now() # returns datetime.datetime objectI can also use the `today method`_ to get the same value
datetime.datetime.today()is a call to the `today method`_ of the `datetime.datetime object`_ from the `datetime module`_, it also returns a `datetime.datetime object`_datetime.datetime.today() # returns datetime.datetime objectdatetime.datetimeobjects have ayearattribute that gives me the value of the current year which means I can do this to get the value of the current yearresult = datetime.datetime.now() this_year = result.yearwhich is the same as
this_year = datetime.datetime.now().yearor
result = datetime.datetime.today() this_year = result.yearwhich is the same as
this_year = datetime.datetime.today().yearthat was a lot of words, they become clearer in the chapters on classes.
test factory with random year_of_birth
I want to use random values in the test to make sure the factory function can handle different values for year_of_birth and always calculates the right age.
I can do that with the random module from The Python Standard Library which is used to make fake random numbers.
RED: make it fail
I use a random integer (a whole number without decimals) for the
year_of_birthvariable1import datetime 2import src.person 3import unittest 4 5 6class TestPerson(unittest.TestCase): 7 8 def test_factory_w_keyword_arguments(self): 9 first_name = 'jane' 10 last_name = 'doe' 11 sex = 'F' 12 # year_of_birth = 1996 13 year_of_birth = random.randint( 14 datetime.datetime.now().year-120, 15 datetime.datetime.now().year 16 ) 17 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 )the terminal is my friend, and shows NameError
NameError: name 'random' is not defined. Did you forget to import 'random'?
GREEN: make it pass
I add an import statement for the random module at the top of
test_person.py1import datetime 2import random 3import src.person 4import unittestthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 30} != {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': X}where
Xis a random ageimport randombrings in an object (everything in Python is an object) for the random module so I can use it intest_person.py. This means I can assume there is arandom.pyfile on the computer that came with Pythonimport random # import object for random.pyrandom.randintis a method of the random module. Okay, this one does not use the same name again.datetime.datetime.now().yeargives me the value of the current yeardatetime.datetime.now().year-120gives me the value of the current year minus120(I assume there are no people older than120, yet)random.randint(datetime.datetime.now().year-120, datetime.datetime.now().year)is a call to the randint method of the random module withdatetime.datetime.now().year-120anddatetime.datetime.now().yearas positional arguments. I can assume there is some definition inrandom.pythat looks like thisdef randint(first_number, second_number): return random number between first_number and second_number including second_numberit returns a random number from 120 years ago, up to and including the current year
anytime I use ctrl/command+s (Windows & Linux/MacOS) to save the file, the test runs again and I get a new random value for the
agekey
I add a calculation for the age with the `today method`_ to the return statement in
person.py1def factory( 2 first_name, last_name, 3 sex, year_of_birth, 4 ): 5 return { 6 'first_name': 'jane', 7 'last_name': 'doe', 8 'sex': 'F', 9 # 'age': 30, 10 'age': ( 11 datetime.datetime.today().year 12 - year_of_birth 13 ), 14 }the terminal is my friend, and shows NameError
NameError: name 'datetime' is not defined. Did you forget to import 'datetime'?I add an import statement for the `datetime module`_ at the top of
person.py1import datetime 2 3 4def factory( 5 first_name, last_name, 6 sex, year_of_birth, 7 ):the test passes because
import datetimebrings in an object (everything in Python is an object) for the `datetime module`_ so I can use it inperson.py.
REFACTOR: make it better
I add a variable for
datetime.datetime.now().yearintest_person.py1import datetime 2import src.person 3import unittest 4import random 5 6 7class TestPerson(unittest.TestCase): 8 9 def test_factory_w_keyword_arguments(self): 10 first_name = 'jane' 11 last_name = 'doe' 12 sex = 'F' 13 14 this_year = datetime.datetime.now().year 15 # year_of_birth = 1996 16 year_of_birth = random.randint( 17 datetime.datetime.now().year-120, 18 datetime.datetime.now().year 19 )I use the variable to remove repetition of
datetime.datetime.now().yearfrom the test9 def test_factory_w_keyword_arguments(self): 10 first_name = 'jane' 11 last_name = 'doe' 12 sex = 'F' 13 14 this_year = datetime.datetime.now().year 15 # year_of_birth = 1996 16 year_of_birth = random.randint( 17 # datetime.datetime.now().year-120, 18 # datetime.datetime.now().year 19 this_year-120, this_year 20 ) 21 22 reality = src.person.factory( 23 first_name=first_name, 24 last_name=last_name, 25 sex=sex, 26 year_of_birth=year_of_birth, 27 ) 28 my_expectation = dict( 29 first_name=first_name, 30 last_name=last_name, 31 sex=sex, 32 # age=2026-year_of_birth, 33 age=( 34 # datetime.datetime.today().year 35 this_year 36 - year_of_birth 37 ), 38 ) 39 self.assertEqual(reality, my_expectation) 40 41 42# Exceptions seenI remove the commented lines
9 def test_factory_w_keyword_arguments(self): 10 first_name = 'jane' 11 last_name = 'doe' 12 sex = 'F' 13 14 this_year = datetime.datetime.now().year 15 year_of_birth = random.randint( 16 this_year-120, this_year 17 ) 18 19 reality = src.person.factory( 20 first_name=first_name, 21 last_name=last_name, 22 sex=sex, 23 year_of_birth=year_of_birth, 24 ) 25 my_expectation = dict( 26 first_name=first_name, 27 last_name=last_name, 28 sex=sex, 29 age=this_year-year_of_birth, 30 ) 31 self.assertEqual(reality, my_expectation) 32 33 34# Exceptions seenI add a git commit message in the other terminal
git commit -am 'use random values for year_of_birth'the terminal shows a summary of the changes then goes back to the command line.
test factory with random sex
I want to use random values in the test to make sure the factory function can handle different values for sex.
RED: make it fail
I go back to the terminal where the tests are running
I add randomness to the
sexvariable intest_person.py9 def test_factory_w_keyword_arguments(self): 10 first_name = 'jane' 11 last_name = 'doe' 12 # sex = 'F' 13 sex = random.choice(('F', 'M')) 14 15 this_year = datetime.datetime.now().yearI use ctrl/command+s (Windows & Linux/MacOS) to run the test a few times
if the value of
sexis'F', the test passesif the value of
sexis'M', the terminal is my friend, and shows AssertionErrorAssertionError: {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': X} != {'first_name': 'jane', 'last_name': 'doe', 'sex': 'M', 'age': X}where
Xis the random agerandomis the random modulerandom.choiceis a method of the random modulerandom.choice(('F', 'M'))is a call to the choice method of the random module with('F', 'M')as input. I can assume there is some definition inrandom.pythat looks like thisdef choice(collection): return random object from collectionit randomly returns
'F'or'M'every time the test runs.I can also use random.choice(
'FM') to get the same result asrandom.choice(('F', 'M'))because a string like a tuple is iterable and random.choice expects an iterable as input
GREEN: make it pass
I use the sex input parameter as the value for the 'sex' key instead of a value that does not change, in the return statement in person.py
4def factory(
5 first_name, last_name,
6 sex, year_of_birth,
7 ):
8 return {
9 'first_name': 'jane',
10 'last_name': 'doe',
11 # 'sex': 'F',
12 'sex': sex,
13 # 'age': 30,
14 'age': (
15 datetime.datetime.today().year
16 - year_of_birth
17 ),
18 }
I use ctrl/command+s (Windows & Linux/MacOS) to run the test a few times and it passes with no random failures.
REFACTOR: make it better
I remove the commented line in
test_person.py9 def test_factory_w_keyword_arguments(self): 10 first_name = 'jane' 11 last_name = 'doe' 12 sex = random.choice(('F', 'M'))I add a git commit message in the other terminal
git commit -am 'use random values for sex'the terminal shows a summary of the changes then goes back to the command line.
test factory with random last name
I want to use random values in the test to make sure the factory function can handle different values for last_name.
RED: make it fail
I go back to the terminal where the tests are running
I use random.choice for the
last_namevariable intest_person.py9 def test_factory_w_keyword_arguments(self): 10 first_name = 'jane' 11 # last_name = 'doe' 12 last_name = random.choice(( 13 'doe', 'smith', 'blow', 'public', 14 )) 15 sex = random.choice(('F', 'M'))I use ctrl/command+s (Windows & Linux/MacOS) to run the test a few times
if the value of
last_nameis'doe', the test passesif the value of
last_nameis NOTdoe, the terminal is my friend, and shows AssertionErrorAssertionError: {'first_name': 'jane', 'last_name': 'doe', 'sex': Y, 'age': X} != {'first_name': 'jane', 'last_name': Z, 'sex': Y, 'age': X}where
Zis the random last name,Yis the random sex andXis the random agerandom.choice(('doe', 'smith', 'blow', 'public',))is a call to the choice method of the random module with('doe', 'smith', 'blow', 'public',)as input. It randomly returns'doe'or'smith'or'blow'or'public'every time the test runs.
GREEN: make it pass
I use the last_name input parameter as the value for the 'last_name' key instead of a value that does not change in the return statement in person.py
4def factory(
5 first_name, last_name,
6 sex, year_of_birth,
7 ):
8 return {
9 'first_name': 'jane',
10 # 'last_name': 'doe',
11 'last_name': last_name,
12 # 'sex': 'F',
13 'sex': sex,
14 # 'age': 30,
15 'age': (
16 datetime.datetime.today().year
17 - year_of_birth
18 ),
19 }
I use ctrl/command+s (Windows & Linux/MacOS) to run the test a few times and it passes with no random failures
REFACTOR: make it better
I remove the commented line from
test_person.py9def test_factory_w_keyword_arguments(self): 10 first_name = 'jane' 11 last_name = random.choice(( 12 'doe', 'smith', 'blow', 'public', 13 )) 14 sex = random.choice(('F', 'M'))I add a git commit message in the other terminal
git commit -am 'use random values for last_name'the terminal shows a summary of the changes then goes back to the command line.
test factory with random first name
I want to use random values in the test to make sure the factory function can handle different values for first_name.
RED: make it fail
I go back to the terminal where the tests are running
I add randomness to the
first_namevariable intest_person.py9 def test_factory_w_keyword_arguments(self): 10 # first_name = 'jane' 11 first_name = random.choice(( 12 'jane', 'joe', 'john', 'person', 13 )) 14 last_name = random.choice(( 15 'doe', 'smith', 'blow', 'public', 16 )) 17 sex = random.choice(('F', 'M'))I use ctrl/command+s (Windows & Linux/MacOS) to run the test a few times
if the value of
first_nameis'jane', the test passesif the value of
first_nameis NOT'jane'the terminal is my friend, and shows AssertionErrorAssertionError: {'first_name': 'jane', 'last_name': Z, 'sex': Y, 'age': X} != {'first_name': A, 'last_name': Z, 'sex': Y, 'age': X}where
Ais the random first name,Zis the random last name,Yis the random sex, andXis the random agerandom.choice(('jane', 'joe', 'john', 'person',))is a call to the choice method of the random module with('jane', 'joe', 'john', 'person',)as input. It randomly returns'jane'or'joe'or'john'or'person'every time the test runs.
GREEN: make it pass
I use the first_name input parameter as the value for the 'first_name' key instead of a value that does not change, in the return statement in person.py
4 def factory(
5 first_name, last_name,
6 sex, year_of_birth,
7 ):
8 return {
9 # 'first_name': 'jane',
10 'first_name': first_name,
11 # 'last_name': 'doe',
12 'last_name': last_name,
13 # 'sex': 'F',
14 'sex': sex,
15 # 'age': 30,
16 'age': (
17 datetime.datetime.today().year
18 - year_of_birth
19 ),
20 }
I use ctrl/command+s (Windows & Linux/MacOS) to run the test a few times and it passes with no random failures.
REFACTOR: make it better
I remove the commented lines
1import datetime 2 3 4def factory( 5 first_name, last_name, 6 sex, year_of_birth, 7 ): 8 return { 9 'first_name': first_name, 10 'last_name': last_name, 11 'sex': sex, 12 'age': ( 13 datetime.datetime.today().year 14 - year_of_birth 15 ), 16 }I remove the commented line from
test_person.py9 def test_factory_w_keyword_arguments(self): 10 first_name = random.choice(( 11 'jane', 'joe', 'john', 'person', 12 )) 13 last_name = random.choice(( 14 'doe', 'smith', 'blow', 'public', 15 )) 16 sex = random.choice(('F', 'M'))I add a git commit message in the other terminal
git commit -am 'use random values for first_name'the terminal shows a summary of the changes then goes back to the command line.
extract pick_one function
The first_name, last_name and sex variables all call the random.choice method. I can add a function for the calls.
I go back to the terminal where the tests are running
I add a function for the calls to the random.choice method
1import datetime 2import src.person 3import unittest 4import random 5 6 7def pick_one(choices): 8 return random.choice(choices) 9 10 11class TestPerson(unittest.TestCase): 12 13 def test_factory_w_keyword_arguments(self):I use the new function for the
first_namevariable13 def test_factory_w_keyword_arguments(self): 14 # first_name = random.choice(( 15 first_name = pick_one(( 16 'jane', 'joe', 'john', 'person', 17 )) 18 last_name = random.choice(( 19 'doe', 'smith', 'blow', 'public', 20 )) 21 sex = random.choice(('F', 'M'))the test is still green. So far, this is exactly the same as random.choice, why would I make a function that is exactly the same?
Each call to the random.choice passes a tuple (an iterable). I want the function to be able to take any number of arguments I send, without knowing how many I will send
13 def test_factory_w_keyword_arguments(self): 14 # first_name = random.choice(( 15 # first_name = pick_one(( 16 first_name = pick_one( 17 'jane', 'joe', 'john', 'person', 18 # )) 19 ) 20 last_name = random.choice(( 21 'doe', 'smith', 'blow', 'public', 22 )) 23 sex = random.choice(('F', 'M'))the terminal is my friend, and shows TypeError
TypeError: pick_one() takes 1 positional argument but 4 were givenbecause the function definition only takes one input and the test sends four.
I add a starred expressions like I did in test_unknown_number_of_arguments so that the
pick_onefunction can take any number of positional arguments7def pick_one(*choices): 8 return random.choice(choices)the test is green again, because
first_name = pick_one('jane', 'joe', 'john', 'person') pick_one(*choices) random.choice(('jane', 'joe', 'john', 'person')) # randomly return # 'jane' or 'joe' or 'john' or 'person'Python reads the positional arguments as a tuple in the function since I used a starred expressions (
*choices).I use the new function for the
last_namevariable13 def test_factory_w_keyword_arguments(self): 14 # first_name = random.choice(( 15 # first_name = pick_one(( 16 first_name = pick_one( 17 'jane', 'joe', 'john', 'person', 18 # )) 19 ) 20 # last_name = random.choice(( 21 last_name = pick_one( 22 'doe', 'smith', 'blow', 'public', 23 # )) 24 ) 25 sex = random.choice(('F', 'M'))the test is still green, because
last_name = pick_one('doe', 'smith', 'blow', 'public') pick_one(*choices) random.choice(('doe', 'smith', 'blow', 'public')) # randomly return # 'doe' or 'smith' or 'blow' or 'public'Python reads the positional arguments as a tuple in the function since I used a starred expressions (
*choices).I use the new function for the
sexvariable13 def test_factory_w_keyword_arguments(self): 14 # first_name = random.choice(( 15 # first_name = pick_one(( 16 first_name = pick_one( 17 'jane', 'joe', 'john', 'person', 18 # )) 19 ) 20 # last_name = random.choice(( 21 last_name = pick_one( 22 'doe', 'smith', 'blow', 'public', 23 # )) 24 ) 25 # sex = random.choice(('F', 'M')) 26 sex = pick_one('F', 'M')still green, because
sex = pick_one('F', 'M'') pick_one(*choices) random.choice(('F', 'M')) # randomly return 'F' or 'M'Python reads the positional arguments as a tuple in the function since I used a starred expressions (
*choices).I remove the commented lines
13 def test_factory_w_keyword_arguments(self): 14 first_name = pick_one( 15 'jane', 'joe', 'john', 'person', 16 ) 17 last_name = pick_one( 18 'doe', 'smith', 'blow', 'public', 19 ) 20 sex = pick_one('F', 'M')I add a git commit message in the other terminal
git commit -am 'extract pick_one function'the terminal shows a summary of the changes then goes back to the command line.
test factory with a dictionary
The difference between the call to the factory function (reality) and the dictionary for my_expectation in the test is - year_of_birth and age. One takes in a value for year_of_birth and the other uses year_of_birth to calculate the age, the other things are the same.
I can use a dictionary to remove the parts that are the same.
RED: make it fail
I go back to the terminal where the tests are running
I add a dictionary for
first_name,last_nameandyear_of_birth13 def test_factory_w_keyword_arguments(self): 14 first_name = pick_one( 15 'jane', 'joe', 'john', 'person', 16 ) 17 last_name = pick_one( 18 'doe', 'smith', 'blow', 'public', 19 ) 20 sex = pick_one('F', 'M') 21 22 a_person = dict( 23 first_name=first_name, 24 last_name=last_name, 25 sex=sex, 26 ) 27 28 this_year = datetime.datetime.now().year 29 year_of_birth = random.randint( 30 this_year-120, this_year 31 )I use the new variable to remove
first_name,last_name, andsexfrom the call tosrc.person.factory13 def test_factory_w_keyword_arguments(self): 14 first_name = pick_one( 15 'jane', 'joe', 'john', 'person', 16 ) 17 last_name = pick_one( 18 'doe', 'smith', 'blow', 'public', 19 ) 20 sex = pick_one('F', 'M') 21 22 a_person = dict( 23 first_name=first_name, 24 last_name=last_name, 25 sex=sex, 26 ) 27 28 this_year = datetime.datetime.now().year 29 year_of_birth = random.randint( 30 this_year-120, this_year 31 ) 32 33 reality = src.person.factory( 34 # first_name=first_name, 35 # last_name=last_name, 36 # sex=sex, 37 a_person, 38 year_of_birth=year_of_birth, 39 ) 40 my_expectation = dict( 41 first_name=first_name, 42 last_name=last_name, 43 sex=sex, 44 age=this_year-year_of_birth, 45 ) 46 self.assertEqual(reality, my_expectation) 47 48 49# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: factory() missing 2 required positional arguments: 'last_name' and 'sex'because this happens
first_name = pick_one('jane', 'joe', 'john', 'person',) last_name = pick_one('doe', 'smith', 'blow', 'public',) sex = pick_one('F', 'M')a_person = dict( first_name=first_name, last_name=last_name, sex=sex, )reality = src.person.factory( a_person, year_of_birth ) src.person.factory( first_name=a_person, year_of_birth=year_of_birth )which raises TypeError since I called the factory function with
a_personas the first positional argument (first_name) and a value foryear_of_birth. The function wants the other required arguments.Python uses the value for
year_of_birthfor theyear_of_birthparameter because the names are the same, even though this given as the second positional argument.
I want the function to take the key-value pairs of the dictionary (
a_person) as keyword arguments.
GREEN: make it pass
I use a double starred expressions for the dictionary like I did in test_unknown_number_of_arguments to make it take the key-value pairs of the dictionary as keyword arguments
33 reality = src.person.factory(
34 # first_name=first_name,
35 # last_name=last_name,
36 # sex=sex,
37 # a_person,
38 **a_person,
39 year_of_birth=year_of_birth,
40 )
41 my_expectation = dict(
42 first_name=first_name,
43 last_name=last_name,
44 sex=sex,
45 age=this_year-year_of_birth,
46 )
47 self.assertEqual(reality, my_expectation)
48
49
50 # Exceptions seen
the test is green again, because this happens
first_name = pick_one('jane', 'joe', 'john', 'person',)
last_name = pick_one('doe', 'smith', 'blow', 'public',)
sex = pick_one('F', 'M')
a_person = dict(
first_name=first_name,
last_name=last_name,
sex=sex,
)
reality = src.person.factory(
a_person,
year_of_birth
)
src.person.factory(
**a_person,
year_of_birth
)
src.person.factory(
first_name=first_name,
last_name=last_name,
sex=sex,
year_of_birth=year_of_birth
)
Python sends the dictionary as keyword arguments since I used a double starred expressions (**a_person).
REFACTOR: make it better
I use the variable for
my_expectation33 reality = src.person.factory( 34 # first_name=first_name, 35 # last_name=last_name, 36 # sex=sex, 37 # a_person, 38 **a_person, 39 year_of_birth=year_of_birth, 40 ) 41 my_expectation = dict( 42 # first_name=first_name, 43 # last_name=last_name, 44 # sex=sex, 45 a_person, 46 age=this_year-year_of_birth, 47 ) 48 self.assertEqual(reality, my_expectation) 49 50 51# Exceptions seenthe test is still green because this happens
first_name = pick_one('jane', 'joe', 'john', 'person',) last_name = pick_one('doe', 'smith', 'blow', 'public',) sex = pick_one('F', 'M')a_person = dict( first_name=first_name, last_name=last_name, sex=sex, )my_expectation = dict( a_person, age=this_year-year_of_birth ) dict( first_name=first_name, last_name=last_name, sex=sex, age=this_year-year_of_birth )the dict constructor can take another dictionary as input and uses the key-value pairs of the dictionary as keyword arguments.
I use the values of
first_name,last_nameand thesexvariables in thea_persondictionary because they are now only used once (bya_person)13 def test_factory_w_keyword_arguments(self): 14 # first_name = pick_one( 15 # 'jane', 'joe', 'john', 'person', 16 # ) 17 # last_name = pick_one( 18 # 'doe', 'smith', 'blow', 'public', 19 # ) 20 # sex = pick_one('F', 'M') 21 22 a_person = dict( 23 # first_name=first_name, 24 # last_name=last_name, 25 # sex=sex, 26 first_name=pick_one( 27 'jane', 'joe', 'john', 'person', 28 ), 29 last_name=pick_one( 30 'doe', 'smith', 'blow', 'public', 31 ), 32 sex=pick_one('F', 'M'), 33 )still green.
I add a function with a tuple of all the names for the test to have more random names to pick from
7def pick_one(*choices): 8 return random.choice(choices) 9 10 11def get_random_name(): 12 return pick_one( 13 'jane', 'joe', 'john', 'person', 14 'doe', 'smith', 'blow', 'public', 15 ) 16 17 18class TestPerson(unittest.TestCase): 19 20 def test_factory_w_keyword_arguments(self):I use the function in the
a_persondictionary20 def test_factory_w_keyword_arguments(self): 21 # first_name = pick_one( 22 # 'jane', 'joe', 'john', 'person', 23 # ) 24 # last_name = pick_one( 25 # 'doe', 'smith', 'blow', 'public', 26 # ) 27 # sex = pick_one('F', 'M') 28 29 a_person = dict( 30 # first_name=first_name, 31 # last_name=last_name, 32 # sex=sex, 33 # first_name=pick_one( 34 # 'jane', 'joe', 'john', 'person', 35 # ), 36 # last_name=pick_one( 37 # 'doe', 'smith', 'blow', 'public', 38 # ), 39 first_name=get_random_name(), 40 last_name=get_random_name(), 41 sex=pick_one('F', 'M'), 42 )green.
I change the assertion to make sure the test works correctly
49 reality = src.person.factory( 50 # first_name=first_name, 51 # last_name=last_name, 52 # sex=sex, 53 # a_person, 54 **a_person, 55 year_of_birth=year_of_birth, 56 ) 57 my_expectation = dict( 58 # first_name=first_name, 59 # last_name=last_name, 60 # sex=sex, 61 a_person, 62 age=this_year-year_of_birth, 63 ) 64 # self.assertEqual(reality, my_expectation) 65 self.assertEqual(reality, {}) 66 67 68# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': A, 'last_name': Z, 'sex': Y, 'age': X} != {}where
Ais the random first name,Zis the random last name,Yis the random sex, andXis the random age.I use ctrl/command+s (Windows & Linux/MacOS) to run the test a few times and I get random values for
'first_name','last_name','sex'and'age'each timeI change the assertion back
49 reality = src.person.factory( 50 # first_name=first_name, 51 # last_name=last_name, 52 # sex=sex, 53 # a_person, 54 **a_person, 55 year_of_birth=year_of_birth, 56 ) 57 my_expectation = dict( 58 # first_name=first_name, 59 # last_name=last_name, 60 # sex=sex, 61 a_person, 62 age=this_year-year_of_birth, 63 ) 64 self.assertEqual(reality, my_expectation) 65 # self.assertEqual(reality, {}) 66 67 68# Exceptions seenI remove the commented lines
20 def test_factory_w_keyword_arguments(self): 21 a_person = dict( 22 first_name=get_random_name(), 23 last_name=get_random_name(), 24 sex=pick_one('F', 'M'), 25 ) 26 27 this_year = datetime.datetime.now().year 28 year_of_birth = random.randint( 29 this_year-120, this_year 30 ) 31 32 reality = src.person.factory( 33 **a_person, 34 year_of_birth=year_of_birth, 35 ) 36 my_expectation = dict( 37 a_person, 38 age=this_year-year_of_birth, 39 ) 40 self.assertEqual(reality, my_expectation) 41 42 43# Exceptions seenI add a git commit message in the other terminal
git commit -am 'extract a_person dictionary'the terminal shows a summary of the changes then goes back to the command line.
test_factory_w_optional_arguments
I want to see what happens when I try to make a person without a value for the last_name argument.
RED: make it fail
I go back to the terminal where the tests are running
I make a copy of test_factory_w_keyword_arguments and paste it below in
test_person.py32 reality = src.person.factory( 33 **a_person, 34 year_of_birth=year_of_birth, 35 ) 36 my_expectation = dict( 37 a_person, 38 age=this_year-year_of_birth, 39 ) 40 self.assertEqual(reality, my_expectation) 41 42 def test_factory_w_keyword_arguments(self): 43 a_person = dict( 44 first_name=get_random_name(), 45 last_name=get_random_name(), 46 sex=pick_one('F', 'M'), 47 ) 48 49 this_year = datetime.datetime.now().year 50 year_of_birth = random.randint( 51 this_year-120, this_year 52 ) 53 54 reality = src.person.factory( 55 **a_person, 56 year_of_birth=year_of_birth, 57 ) 58 my_expectation = dict( 59 a_person, 60 age=this_year-year_of_birth, 61 ) 62 self.assertEqual(reality, my_expectation) 63 64 65# Exceptions seenI change the name of the new test to test_factory_w_optional_arguments
32 reality = src.person.factory( 33 **a_person, 34 year_of_birth=year_of_birth, 35 ) 36 my_expectation = dict( 37 a_person, 38 age=this_year-year_of_birth, 39 ) 40 self.assertEqual(reality, my_expectation) 41 42 def test_factory_w_optional_arguments(self): 43 a_person = dict( 44 first_name=get_random_name(), 45 last_name=get_random_name(), 46 sex=pick_one('F', 'M'), 47 )I comment out the
last_namekey-value pair in thea_persondictionary42 def test_factory_w_optional_arguments(self): 43 a_person = dict( 44 first_name=get_random_name(), 45 # last_name=get_random_name(), 46 sex=pick_one('F', 'M'), 47 )the terminal is my friend, and shows TypeError
TypeError: factory() missing 1 required positional argument: 'last_name'because this test no longer gives a value for
last_namewhen it calls the factory function, I have to makelast_namea choice, not a requirement.
GREEN: make it pass
I add a default value for
last_nameto make it optional, in the factory function inperson.py4def factory( 5 # first_name, last_name, 6 first_name, last_name=None, 7 sex, year_of_birth, 8 ):the terminal is my friend, and shows SyntaxError
SyntaxError: parameter without a default follows parameter with a defaultbecause parameters without default values must come before parameters with default values.
I add SyntaxError to the list of Exceptions seen, in
test_person.py65# Exceptions seen 66# AssertionError 67# NameError 68# AttributeError 69# TypeError 70# SyntaxErrorI add a default value for
sexto make it optional, in the factory function inperson.py4def factory( 5 # first_name, last_name, 6 first_name, last_name=None, 7 # sex, year_of_birth, 8 sex=None, year_of_birth, 9 ):the terminal is my friend, and shows SyntaxError
SyntaxError: parameter without a default follows parameter with a defaultbecause parameters without default values must come before parameters with default values.
I add a default value for
year_of_birthto make it optional4def factory( 5 # first_name, last_name, 6 first_name, last_name=None, 7 # sex, year_of_birth, 8 # sex=None, year_of_birth, 9 sex=None, year_of_birth=None, 10 ):the terminal is my friend, and shows AssertionError
AssertionError: {'first_name': Z, 'last_name': None, 'sex': Y, 'age': X} != {'first_name': Z, 'sex': Y, 'age': X}because this happens when the factory function is called without a value for
last_namea_person = dict( first_name=get_random_name(), sex=pick_one('F', 'M'), )reality = src.person.factory( **a_person, year_of_birth=year_of_birth, ) src.person.factory( first_name=get_random_name(), sex=pick_one('F', 'M'), year_of_birth=year_of_birth, ) src.person.factory( first_name=get_random_name(), sex=pick_one('F', 'M'), last_name=None, # use the default value year_of_birth=year_of_birth, ) # the factory function returns {'first_name': Z, 'last_name': None, 'sex': Y, 'age': X}my_expectation = dict( a_person, age=this_year-year_of_birth, ) dict( first_name=get_random_name(), sex=pick_one('F', 'M'), year_of_birth=year_of_birth, ) # the dict constructor returns {'first_name': Z, 'sex': Y, 'age': X}which raises AssertionError since the factory function returns a dictionary with a
'last_name'key, and the assertion expects a dictionary without that keyXis the random age,Yis the random sex andZis the random first name
I add a key-value pair for
last_nametomy_expectationin test_factory_w_optional_arguments intest_person.py54 reality = src.person.factory( 55 **a_person, 56 year_of_birth=year_of_birth, 57 ) 58 my_expectation = dict( 59 a_person, 60 last_name='doe', 61 age=this_year-year_of_birth, 62 ) 63 self.assertEqual(reality, my_expectation) 64 65 66# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': Z, 'last_name': None, 'sex': Y, 'age': X} != {'first_name': Z, 'sex': Y, 'last_name': 'doe', 'age': X}because the factory function returns a dictionary with a value of None for
last_nameand the assertion expects'doe'.I change the default value for
last_namein the factory function inperson.py4def factory( 5 # first_name, last_name, 6 # first_name, last_name=None, 7 # sex, year_of_birth, 8 # sex=None, year_of_birth, 9 first_name, last_name='doe', 10 sex=None, year_of_birth=None, 11 ):the test passes because the default value for the
last_nameparameter of the function is'doe'. This means thatsrc.person.factory( first_name=first_name, sex=sex, year_of_birth=year_of_birth, )is the same as
src.person.factory( first_name=first_name, sex=sex, year_of_birth=year_of_birth, last_name='doe', )because a function uses the default value for a parameter when it is called without the parameter.
REFACTOR: make it better
I comment out the
sexkey in test_factory_w_optional_arguments to see what happens when I call the factory function without it, intest_person.py42 def test_factory_w_optional_arguments(self): 43 this_year = datetime.datetime.now().year 44 year_of_birth = random.randint( 45 this_year-120, this_year 46 ) 47 48 a_person = dict( 49 first_name=get_random_name(), 50 # last_name=get_random_name(), 51 # sex=pick_one('F', 'M'), 52 )the terminal is my friend, and shows AssertionError
AssertionError: {'first_name': Y, 'last_name': 'doe, 'sex': None, 'age': X} != {'first_name': Y, 'last_name': 'doe', 'age': X}because this happens when the factory function is called without a value for
sexa_person = dict(first_name=get_random_name())reality = src.person.factory( **a_person, year_of_birth=year_of_birth, ) src.person.factory( first_name=get_random_name(), year_of_birth=year_of_birth, ) src.person.factory( first_name=get_random_name(), sex=None, # use the default value last_name='doe', # use the default value year_of_birth=year_of_birth, ) # the factory function returns {'first_name': Y, 'last_name': 'doe, 'sex': None, 'age': X}my_expectation = dict( a_person, last_name='doe', age=this_year-year_of_birth, ) dict( first_name=get_random_name(), last_name='doe', age=this_year-year_of_birth, ) # the dict constructor returns {'first_name': Y, 'last_name': 'doe', 'age': X}which raises AssertionError since the factory function returns a dictionary with a
'sex'key, and the assertion expects a dictionary without that keyXis the random age,Yis the random first name
I add a key-value pair for
sextomy_expectationin test_factory_w_optional_arguments54 reality = src.person.factory( 55 **a_person, 56 year_of_birth=year_of_birth, 57 ) 58 my_expectation = dict( 59 a_person, 60 last_name='doe', 61 sex='M', 62 age=this_year-year_of_birth, 63 ) 64 self.assertEqual(reality, my_expectation) 65 66 67# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': Y, 'last_name': 'doe', 'sex': None, 'age': X} != {'first_name': Y, 'last_name': 'doe', 'sex': 'M', 'age': X}because the factory function returns a dictionary with a value of None for
sexand the assertion expects'M'.I change the default value for
sexin the factory function inperson.py4def factory( 5 # first_name, last_name, 6 # first_name, last_name=None, 7 # sex, year_of_birth, 8 # sex=None, year_of_birth, 9 first_name, last_name='doe', 10 # sex=None, year_of_birth=None, 11 sex='M', year_of_birth=None, 12 ):the test passes because the default value for the
sexparameter of the function is'M'. This means thatsrc.person.factory( first_name=first_name, year_of_birth=year_of_birth, )is the same as
src.person.factory( first_name=first_name, year_of_birth=year_of_birth, last_name='doe', sex='M', )because a function uses the default value for a parameter when it is called without the parameter.
I no longer need the
a_persondictionary in test_factory_w_optional_arguments because it only has one key, I can use a variable forfirst_nameinstead42 def test_factory_w_optional_arguments(self): 43 first_name = get_random_name() 44 a_person = dict( 45 first_name=get_random_name(), 46 # last_name=get_random_name(), 47 # sex=pick_one('F', 'M'), 48 )I use the variable for
first_namein the call tosrc.person.factory55 reality = src.person.factory( 56 **a_person, 57 first_name=first_name, 58 year_of_birth=year_of_birth, 59 ) 60 my_expectation = dict( 61 a_person, 62 last_name='doe', 63 sex='M', 64 age=this_year-year_of_birth, 65 ) 66 self.assertEqual(reality, my_expectation) 67 68 69# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: src.person.factory() got multiple values for keyword argument 'first_name'because this happens
first_name = get_random_name() a_person = dict(first_name=get_random_name())reality = src.person.factory( **a_person, first_name=first_name, year_of_birth=year_of_birth, ) src.person.factory( first_name=get_random_nam(), first_name=first_name, # repeated keyword year_of_birth=year_of_birth )the
a_persondictionary has a key calledfirst_name,src.person.factorygets called with the same keyword argument twice.I comment out
**a_person,55 reality = src.person.factory( 56 # **a_person, 57 first_name=first_name, 58 year_of_birth=year_of_birth, 59 ) 60 my_expectation = dict( 61 a_person, 62 last_name='doe', 63 sex='M', 64 age=this_year-year_of_birth, 65 ) 66 self.assertEqual(reality, my_expectation) 67 68 69# Exceptions seenI use ctrl/command+s (Windows & Linux/MacOS) to run the test a few times
if the values for
first_namematch, the test passesif the values for
first_nameare not the same, the terminal shows AssertionErrorAssertionError: {'first_name': Y, 'last_name': 'doe', 'sex': 'M', 'age': X} != {'first_name': Z, 'last_name': 'doe', 'sex': 'M', 'age': X}where
YandZare the random first names, andXis the random age
I add a key-value pair for
first_nametomy_expectationin test_factory_w_optional_arguments55 reality = src.person.factory( 56 # **a_person, 57 first_name=first_name, 58 year_of_birth=year_of_birth, 59 ) 60 my_expectation = dict( 61 a_person, 62 first_name=first_name, 63 last_name='doe', 64 sex='M', 65 age=this_year-year_of_birth, 66 ) 67 self.assertEqual(reality, my_expectation) 68 69 70# Exceptions seenthe test passes.
I remove the commented lines and the
a_persondictionary42 def test_factory_w_optional_arguments(self): 43 first_name = get_random_name() 44 45 this_year = datetime.datetime.now().year 46 year_of_birth = random.randint( 47 this_year-120, this_year 48 ) 49 50 reality = src.person.factory( 51 first_name=first_name, 52 year_of_birth=year_of_birth, 53 ) 54 my_expectation = dict( 55 first_name=first_name, 56 last_name='doe', 57 sex='M', 58 age=this_year-year_of_birth, 59 ) 60 self.assertEqual(reality, my_expectation) 61 62 63# Exceptions seenthe test is still green.
I remove the commented lines from
person.py4def factory( 5 first_name, last_name='doe', 6 sex='M', year_of_birth=None, 7 ): 8 return { 9 'first_name': first_name, 10 'last_name': last_name, 11 'sex': sex, 12 'age': ( 13 datetime.datetime.today().year 14 - year_of_birth 15 ), 16 }I add a git commit message in the other terminal
git commit -am \ 'add test_factory_w_optional_arguments'the terminal shows a summary of the changes then goes back to the command line.
test_factory_person_says_hello
I have a function that takes in first_name, last_name, sex and year_of_birth for a person and returns a dictionary with first_name, last_name, sex and age (based on the year_of_birth) as keys.
What if I want the person to say hello, How would I do that? I can write a function that takes in a person (dictionary) and returns a message (string).
RED: make it fail
I add a new test to
test_person.py50 reality = src.person.factory( 51 first_name=first_name, 52 year_of_birth=year_of_birth, 53 ) 54 my_expectation = dict( 55 first_name=first_name, 56 last_name='doe', 57 sex='M', 58 age=this_year-year_of_birth, 59 ) 60 self.assertEqual(reality, my_expectation) 61 62 def test_factory_person_says_hello(self): 63 joe = src.person.factory( 64 first_name='joe', 65 last_name='blow', 66 year_of_birth=1996, 67 ) 68 69 reality = src.person.say_hello(joe) 70 my_expectation = None 71 self.assertEqual(reality, my_expectation) 72 73 74# Exceptions seenthe terminal is my friend, and shows AttributeError
AttributeError: module 'src.person' has no attribute 'say_hello'because
person.pydoes not have a function namedsay_hello, yet.
GREEN: make it pass
I add the function to
person.py4def factory( 5 first_name, last_name='doe', 6 sex='M', year_of_birth=None, 7 ): 8 return { 9 'first_name': first_name, 10 'last_name': last_name, 11 'sex': sex, 12 'age': ( 13 datetime.datetime.today().year 14 - year_of_birth 15 ), 16 } 17 18 19def say_hello(): 20 return Nonethe terminal is my friend, and shows TypeError
TypeError: say_hello() takes 0 positional arguments but 1 was givenbecause the definition for
say_hellodoes not allow inputs and the test called the function with a positional argument (person).I add a name to the definition
19# def say_hello(): 20def say_hello(person): 21 return Nonethe test passes.
REFACTOR: make it better
I want the say_hello function to return a string for the person (dictionary) I give as input
I change
my_expectationto an f-string in test_factory_person_says_hello intest_person.py62 def test_factory_person_says_hello(self): 63 joe = src.person.factory( 64 first_name='joe', 65 last_name='blow', 66 year_of_birth=1996, 67 ) 68 69 reality = src.person.say_hello(joe) 70 # my_expectation = None 71 my_expectation = ( 72 'Hello, my name is joe blow and I am' 73 f' {datetime.datetime.now().year-1996}' 74 ) 75 self.assertEqual(reality, my_expectation) 76 77 78# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: None != 'Hello, my name is joe blow and I am 30'I copy (ctrl/command+c) the value from the terminal and paste it (ctrl/command+v) in the return statement in
person.py19# def say_hello(): 20def say_hello(person): 21 # return None 22 return 'Hello, my name is joe blow and I am 30'the test passes.
I add an assertion for another person, to test_factory_person_says_hello in
test_person.py69 reality = src.person.say_hello(joe) 70 # my_expectation = None 71 my_expectation = ( 72 'Hello, my name is joe blow and I am' 73 f' {datetime.datetime.now().year-1996}' 74 ) 75 self.assertEqual(reality, my_expectation) 76 77 jane = src.person.factory( 78 first_name='jane', 79 sex='F', 80 year_of_birth=1991, 81 ) 82 83 reality = src.person.say_hello(jane) 84 my_expectation = ( 85 'Hello, my name is jane doe and I am' 86 f' {datetime.datetime.now().year-1991}' 87 ) 88 self.assertEqual(reality, my_expectation) 89 90 91# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: 'Hello, my name is joe blow and I am 30' != 'Hello, my name is jane doe and I am 35'I have to make sure the
say_hellofunction uses the values of thepersondictionary to make the message. I can do that with the get method of dictionaries.I change the string to an f-string with the value for the
first_nameinperson.py19# def say_hello(): 20def say_hello(person): 21 first_name = person.get('first_name') 22 # return None 23 # return 'Hello, my name is joe blow and I am 30' 24 return ( 25 f'Hello, my name is {first_name}' 26 ' blow and I am 30' 27 )the terminal is my friend, and shows AssertionError
AssertionError: 'Hello, my name is jane blow and I am 30' != 'Hello, my name is jane doe and I am 35'the values for
first_nameare the same becausethis happens when
jane = src.person.factory(first_name='jane', sex='F', year_of_birth=1991)runsjane = src.person.factory( first_name='jane', sex='F', year_of_birth=1991, ) src.person.factory( first_name='jane', last_name='doe', # use the default value year_of_birth=1991, sex='F' ) # the factory function returns {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 35}this happens in
say_hellowhensrc.person.say_hello(jane)runsfirst_name = person.get('first_name') jane.get('first_name') { 'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 35 }.get('first_name') # the get method returns 'jane'return ( f'Hello, my name is {first_name}' ' blow and I am 30' )
src.person.say_hello(jane) # the say_hello function returns 'Hello, my name is jane blow and I am 30'which raises AssertionError since the values for
last_nameandageare different.I use the get method of the dictionary to get the value for the
last_namekey, then add it to the return statement19# def say_hello(): 20def say_hello(person): 21 first_name = person.get('first_name') 22 last_name = person.get('last_name') 23 # return None 24 # return 'Hello, my name is joe blow and I am 30' 25 return ( 26 # f'Hello, my name is {first_name}' 27 # ' blow and I am 30' 28 f'Hello, my name is {first_name} {last_name}' 29 ' and I am 30' 30 )the terminal is my friend, and shows AssertionError
AssertionError: 'Hello, my name is jane doe and I am 30' != 'Hello, my name is jane doe and I am 35'the values for
last_nameare the same becausethis happens when
jane = src.person.factory(first_name='jane', sex='F', year_of_birth=1991)runsjane = src.person.factory( first_name='jane', sex='F', year_of_birth=1991, ) src.person.factory( first_name='jane', last_name='doe', # use the default value year_of_birth=1991, sex='F' ) # the factory function returns {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 35}this happens in
say_hellowhensrc.person.say_hello(jane)runsfirst_name = person.get('first_name') # the get method returns 'jane'last_name = person.get('last_name') jane.get('last_name') { 'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 35 }.get('last_name') # the get method returns 'doe'return ( f'Hello, my name is {first_name} {last_name}' ' and I am 30' )
src.person.say_hello(jane) # the say_hello function returns 'Hello, my name is jane doe and I am 30'which raises AssertionError since the values for
ageare different.I use the get method of the dictionary to get the value for the
agekey , then add it to the return statement19# def say_hello(): 20def say_hello(person): 21 first_name = person.get('first_name') 22 last_name = person.get('last_name') 23 age = person.get('age') 24 # return None 25 # return 'Hello, my name is joe blow and I am 30' 26 return ( 27 # f'Hello, my name is {first_name}' 28 # ' blow and I am 30' 29 f'Hello, my name is {first_name} {last_name}' 30 # ' and I am 30' 31 f' and I am {age}' 32 )the test passes because
this happens when
jane = src.person.factory(first_name='jane', sex='F', year_of_birth=1991)runsjane = src.person.factory( first_name='jane', sex='F', year_of_birth=1991, ) src.person.factory( first_name='jane', last_name='doe', # use the default value year_of_birth=1991, sex='F' ) # the factory function returns {'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 35}this happens in
say_hellowhensrc.person.say_hello(jane)runsfirst_name = person.get('first_name') # the get method returns 'jane' last_name = person.get('last_name') # the get method returns 'doe'age = person.get('age') jane.get('age') { 'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 35 }.get('age') # the get method returns 35return ( f'Hello, my name is {first_name} {last_name}' f' and I am {age}' )
src.person.say_hello(jane) # the say_hello function returns 'Hello, my name is jane doe and I am 35'I remove the commented lines
19def say_hello(person): 20 first_name = person.get('first_name') 21 last_name = person.get('last_name') 22 age = person.get('age') 23 24 return ( 25 f'Hello, my name is {first_name} {last_name}' 26 f' and I am {age}' 27 )I add an assertion for a new person to test_factory_person_says_hello in
test_person.py77 jane = src.person.factory( 78 first_name='jane', 79 sex='F', 80 year_of_birth=1991, 81 ) 82 83 reality = src.person.say_hello(jane) 84 my_expectation = ( 85 'Hello, my name is jane doe and I am' 86 f' {datetime.datetime.now().year-1991}' 87 ) 88 self.assertEqual(reality, my_expectation) 89 90 john = src.person.factory( 91 first_name='john', 92 last_name='smith', 93 year_of_birth=1580, 94 ) 95 96 reality = src.person.say_hello(john) 97 my_expectation = ( 98 'Hello, my name is jane doe and I am' 99 f' {datetime.datetime.now().year-1991}' 100 ) 101 self.assertEqual(reality, my_expectation) 102 103 104# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: 'Hello, my name is john smith and I am 446' != 'Hello, my name is jane doe and I am 35'I change
my_expectationto matchrealityforjohn90 john = src.person.factory( 91 first_name='john', 92 last_name='smith', 93 year_of_birth=1580, 94 ) 95 96 reality = src.person.say_hello(john) 97 my_expectation = ( 98 # 'Hello, my name is jane doe and I am' 99 # f' {datetime.datetime.now().year-1991}' 100 'Hello, my name is john smith and I am' 101 f' {datetime.datetime.now().year-1580}' 102 ) 103 self.assertEqual(reality, my_expectation) 104 105 106# Exceptions seenthe test passes.
I add an assertion for one more person
96 reality = src.person.say_hello(john) 97 my_expectation = ( 98 # 'Hello, my name is jane doe and I am' 99 # f' {datetime.datetime.now().year-1991}' 100 'Hello, my name is john smith and I am' 101 f' {datetime.datetime.now().year-1580}' 102 ) 103 self.assertEqual(reality, my_expectation) 104 105 mary = src.person.factory( 106 first_name='mary', 107 last_name='public', 108 year_of_birth=2000, 109 sex='F', 110 ) 111 112 reality = src.person.say_hello(mary) 113 my_expectation = ( 114 'Hello, my name is john smith and I am' 115 f' {datetime.datetime.now().year-1580}' 116 ) 117 self.assertEqual(reality, my_expectation) 118 119 120# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: 'Hello, my name is mary public and I am 26' != 'Hello, my name is john smith and I am 446'I change
my_expectationto matchrealityformary105 mary = src.person.factory( 106 first_name='mary', 107 last_name='public', 108 year_of_birth=2000, 109 sex='F', 110 ) 111 112 reality = src.person.say_hello(mary) 113 my_expectation = ( 114 # 'Hello, my name is john smith and I am' 115 # f' {datetime.datetime.now().year-1580}' 116 'Hello, my name is mary public and I am' 117 f' {datetime.datetime.now().year-2000}' 118 ) 119 self.assertEqual(reality, my_expectation) 120 121 122# Exceptions seenthe test passes.
I add a variable for
datetime.datetime.now().year62 def test_factory_person_says_hello(self): 63 this_year = datetime.datetime.now().year 64 65 joe = src.person.factory( 66 first_name='joe', 67 last_name='blow', 68 year_of_birth=1996, 69 )I use the variable to remove repetition of
datetime.datetime.now().yearfrom the test62 def test_factory_person_says_hello(self): 63 this_year = datetime.datetime.now().year 64 65 joe = src.person.factory( 66 first_name='joe', 67 last_name='blow', 68 year_of_birth=1996, 69 ) 70 71 reality = src.person.say_hello(joe) 72 # my_expectation = None 73 my_expectation = ( 74 'Hello, my name is joe blow and I am' 75 # f' {datetime.datetime.now().year-1996}' 76 f' {this_year-1996}' 77 ) 78 self.assertEqual(reality, my_expectation) 79 80 jane = src.person.factory( 81 first_name='jane', 82 sex='F', 83 year_of_birth=1991, 84 ) 85 86 reality = src.person.say_hello(jane) 87 my_expectation = ( 88 'Hello, my name is jane doe and I am' 89 # f' {datetime.datetime.now().year-1991}' 90 f' {this_year-1991}' 91 ) 92 self.assertEqual(reality, my_expectation) 93 94 john = src.person.factory( 95 first_name='john', 96 last_name='smith', 97 year_of_birth=1580, 98 ) 99 100 reality = src.person.say_hello(john) 101 my_expectation = ( 102 # 'Hello, my name is jane doe and I am' 103 # f' {datetime.datetime.now().year-1991}' 104 'Hello, my name is john smith and I am' 105 # f' {datetime.datetime.now().year-1580}' 106 f' {this_year-1580}' 107 ) 108 self.assertEqual(reality, my_expectation) 109 110 mary = src.person.factory( 111 first_name='mary', 112 last_name='public', 113 year_of_birth=2000, 114 sex='F', 115 ) 116 117 reality = src.person.say_hello(mary) 118 my_expectation = ( 119 # 'Hi my name is john smith and I am' 120 # f' {datetime.datetime.now().year-1580}' 121 'Hello, my name is mary public and I am' 122 # f' {datetime.datetime.now().year-2000}' 123 f' {this_year-2000}' 124 ) 125 self.assertEqual(reality, my_expectation) 126 127 128# Exceptions seenthe test is still green.
I add variables for
'mary','public',2000and the age calculation ofmary100 reality = src.person.say_hello(john) 101 my_expectation = ( 102 # 'Hello, my name is jane doe and I am' 103 # f' {datetime.datetime.now().year-1991}' 104 'Hello, my name is john smith and I am' 105 # f' {datetime.datetime.now().year-1580}' 106 f' {this_year-1580}' 107 ) 108 self.assertEqual(reality, my_expectation) 109 110 first_name = 'mary' 111 last_name = 'public' 112 year_of_birth = 2000 113 age = this_year - year_of_birth 114 115 mary = src.person.factory( 116 first_name='mary', 117 last_name='public', 118 year_of_birth=2000, 119 sex='F', 120 )I use the variables to remove repetition of
'mary','public',2000and the age calculation104 first_name = 'mary' 105 last_name = 'public' 106 year_of_birth = 2000 107 age = this_year - year_of_birth 108 109 mary = src.person.factory( 110 # first_name='mary', 111 # last_name='public', 112 # year_of_birth=2000, 113 first_name=first_name, 114 last_name=last_name, 115 year_of_birth=year_of_birth, 116 sex='F', 117 ) 118 119 reality = src.person.say_hello(mary) 120 my_expectation = ( 121 # 'Hi my name is john smith and I am' 122 # f' {datetime.datetime.now().year-1580}' 123 # 'Hello, my name is mary public and I am' 124 # f' {datetime.datetime.now().year-2000}' 125 # f' {this_year-2000}' 126 f'Hello, my name is {first_name}' 127 f' {last_name} and I am {age}' 128 ) 129 self.assertEqual(reality, my_expectation) 130 131 132# Exceptions seenstill green.
I add variables for
'john','smith',1580and the age calculation ofjohn86 reality = src.person.say_hello(jane) 87 my_expectation = ( 88 'Hello, my name is jane doe and I am' 89 # f' {datetime.datetime.now().year-1991}' 90 f' {this_year-1991}' 91 ) 92 self.assertEqual(reality, my_expectation) 93 94 first_name = 'john' 95 last_name = 'smith' 96 year_of_birth = 1580 97 age = this_year - year_of_birth 98 99 john = src.person.factory( 100 first_name='john', 101 last_name='smith', 102 year_of_birth=1580, 103 )I use the variables to remove repetition of
'john','smith',1580and the age calculation94 first_name = 'john' 95 last_name = 'smith' 96 year_of_birth = 1580 97 age = this_year - year_of_birth 98 99 john = src.person.factory( 100 # first_name='john', 101 # last_name='smith', 102 # year_of_birth=1580, 103 first_name=first_name, 104 last_name=last_name, 105 year_of_birth=year_of_birth, 106 ) 107 108 reality = src.person.say_hello(john) 109 my_expectation = ( 110 # 'Hello, my name is jane doe and I am' 111 # f' {datetime.datetime.now().year-1991}' 112 # 'Hello, my name is john smith and I am' 113 # f' {datetime.datetime.now().year-1580}' 114 # f' {this_year-1580}' 115 f'Hello, my name is {first_name}' 116 f' {last_name} and I am {age}' 117 ) 118 self.assertEqual(reality, my_expectation)green.
I add variables for
'jane',1991and the age calculation ofjane71 reality = src.person.say_hello(joe) 72 # my_expectation = None 73 my_expectation = ( 74 'Hello, my name is joe blow and I am' 75 # f' {datetime.datetime.now().year-1996}' 76 f' {this_year-1996}' 77 ) 78 self.assertEqual(reality, my_expectation) 79 80 first_name = 'jane' 81 year_of_birth = 1991 82 age = this_year - year_of_birth 83 84 jane = src.person.factory( 85 first_name='jane', 86 sex='F', 87 year_of_birth=1991, 88 )I use the variables to remove repetition of
'jane',1991and the age calculation80 first_name = 'jane' 81 year_of_birth = 1991 82 age = this_year - year_of_birth 83 84 jane = src.person.factory( 85 # first_name='jane', 86 first_name=first_name, 87 sex='F', 88 # year_of_birth=1991, 89 year_of_birth=year_of_birth 90 ) 91 92 reality = src.person.say_hello(jane) 93 my_expectation = ( 94 # 'Hello, my name is jane doe and I am' 95 # f' {datetime.datetime.now().year-1991}' 96 # f' {this_year-1991}' 97 f'Hello, my name is {first_name}' 98 f' doe and I am {age}' 99 ) 100 self.assertEqual(reality, my_expectation)still green.
I add variables for
'joe','blow',1996and the age calculation forjoe62 def test_factory_person_says_hello(self): 63 this_year = datetime.datetime.now().year 64 65 first_name = 'joe' 66 last_name = 'blow' 67 year_of_birth = 1996 68 age = this_year - year_of_birth 69 70 joe = src.person.factory( 71 first_name='joe', 72 last_name='blow', 73 year_of_birth=1996, 74 )I use the variables to remove repetition of
'joe','blow',1996and the age calculation62 def test_factory_person_says_hello(self): 63 this_year = datetime.datetime.now().year 64 65 first_name = 'joe' 66 last_name = 'blow' 67 year_of_birth = 1996 68 age = this_year - year_of_birth 69 70 joe = src.person.factory( 71 # first_name='joe', 72 # last_name='blow', 73 # year_of_birth=1996, 74 first_name=first_name, 75 last_name=last_name, 76 year_of_birth=year_of_birth, 77 ) 78 79 reality = src.person.say_hello(joe) 80 # my_expectation = None 81 my_expectation = ( 82 # 'Hello, my name is joe blow and I am' 83 # f' {datetime.datetime.now().year-1996}' 84 # f' {this_year-1996}' 85 f'Hello, my name is {first_name}' 86 f' {last_name} and I am {age}' 87 ) 88 self.assertEqual(reality, my_expectation)the test is still green.
I remove the commented lines
62 def test_factory_person_says_hello(self): 63 this_year = datetime.datetime.now().year 64 65 first_name = 'joe' 66 last_name = 'blow' 67 year_of_birth = 1996 68 age = this_year - year_of_birth 69 70 joe = src.person.factory( 71 first_name=first_name, 72 last_name=last_name, 73 year_of_birth=year_of_birth, 74 ) 75 76 reality = src.person.say_hello(joe) 77 my_expectation = ( 78 f'Hello, my name is {first_name}' 79 f' {last_name} and I am {age}' 80 ) 81 self.assertEqual(reality, my_expectation) 82 83 first_name = 'jane' 84 year_of_birth = 1991 85 age = this_year - year_of_birth 86 87 jane = src.person.factory( 88 first_name=first_name, 89 sex='F', 90 year_of_birth=year_of_birth 91 ) 92 93 reality = src.person.say_hello(jane) 94 my_expectation = ( 95 f'Hello, my name is {first_name}' 96 f' doe and I am {age}' 97 ) 98 self.assertEqual(reality, my_expectation) 99 100 first_name = 'john' 101 last_name = 'smith' 102 year_of_birth = 1580 103 age = this_year - year_of_birth 104 105 john = src.person.factory( 106 first_name=first_name, 107 last_name=last_name, 108 year_of_birth=year_of_birth, 109 ) 110 111 reality = src.person.say_hello(john) 112 my_expectation = ( 113 f'Hello, my name is {first_name}' 114 f' {last_name} and I am {age}' 115 ) 116 self.assertEqual(reality, my_expectation) 117 118 first_name = 'mary' 119 last_name = 'public' 120 year_of_birth = 2000 121 age = this_year - year_of_birth 122 123 mary = src.person.factory( 124 first_name=first_name, 125 last_name=last_name, 126 year_of_birth=year_of_birth, 127 sex='F', 128 ) 129 130 reality = src.person.say_hello(mary) 131 my_expectation = ( 132 f'Hello, my name is {first_name}' 133 f' {last_name} and I am {age}' 134 ) 135 self.assertEqual(reality, my_expectation) 136 137 138# Exceptions seenI add a git commit message in the other terminal
git commit -am \ 'add test_factory_person_says_hello'the terminal shows a summary of the changes then goes back to the command line.
extract get_current_year function
Each test uses datetime.datetime.now().year to get the current year.
I go back to the terminal where the tests are running
I add a function that returns the value of the current year
11def get_random_name(): 12 return pick_one( 13 'jane', 'joe', 'john', 'person', 14 'doe', 'smith', 'blow', 'public', 15 ) 16 17 18def get_current_year(): 19 return datetime.datetime.now().year 20 21 22class TestPerson(unittest.TestCase): 23 24 def test_factory_w_keyword_arguments(self):I use the new function for
this_yearin test_factory_w_keyword_arguments24 def test_factory_w_keyword_arguments(self): 25 a_person = dict( 26 first_name=get_random_name(), 27 last_name=get_random_name(), 28 sex=pick_one('F', 'M'), 29 ) 30 31 # this_year = datetime.datetime.now().year 32 this_year = get_current_year() 33 year_of_birth = random.randint( 34 this_year-120, this_year 35 )the test is still green.
I use the function for
this_yearin test_factory_w_optional_arguments47 def test_factory_w_optional_arguments(self): 48 first_name = get_random_name() 49 50 # this_year = datetime.datetime.now().year 51 this_year = get_current_year() 52 year_of_birth = random.randint( 53 this_year-120, this_year 54 )still green.
I use the function for
this_yearin test_factory_person_says_hello68 def test_factory_person_says_hello(self): 69 # this_year = datetime.datetime.now().year 70 this_year = get_current_year() 71 72 first_name = 'joe' 73 last_name = 'blow' 74 year_of_birth = 1996 75 age = this_year - year_of_birthgreen.
I add a git commit message in the other terminal
git commit -am \ 'extract get_current_year function'the terminal shows a summary of the changes then goes back to the command line.
extract calculate_age function
Each assertion in every test has a calculation for the age
I go back to the terminal where the tests are running
I add a function to calculate age
18def get_current_year(): 19 return datetime.datetime.now().year 20 21 22def calculate_age(year_of_birth): 23 return ( 24 get_current_year() 25 - year_of_birth 26 ) 27 28 29class TestPerson(unittest.TestCase): 30 31 def test_factory_w_keyword_arguments(self):I add a call to the function for the value of
ageinmy_expectationin test_factory_w_keyword_arguments44 reality = src.person.factory( 45 **a_person, 46 year_of_birth=year_of_birth, 47 ) 48 my_expectation = dict( 49 a_person, 50 # age=this_year-year_of_birth, 51 age=calculate_age(year_of_birth), 52 ) 53 self.assertEqual(reality, my_expectation) 54 55 def test_factory_w_optional_arguments(self):the test is still green.
I remove the commented lines
31 def test_factory_w_keyword_arguments(self): 32 a_person = dict( 33 first_name=get_random_name(), 34 last_name=get_random_name(), 35 sex=pick_one('F', 'M'), 36 ) 37 38 this_year = get_current_year() 39 year_of_birth = random.randint( 40 this_year-120, this_year 41 ) 42 43 reality = src.person.factory( 44 **a_person, 45 year_of_birth=year_of_birth, 46 ) 47 my_expectation = dict( 48 a_person, 49 age=calculate_age(year_of_birth), 50 ) 51 self.assertEqual(reality, my_expectation) 52 53 def test_factory_w_optional_arguments(self):I add a call to the function for the value of
ageinmy_expectationin test_factory_w_optional_arguments62 reality = src.person.factory( 63 first_name=first_name, 64 year_of_birth=year_of_birth, 65 ) 66 my_expectation = dict( 67 first_name=first_name, 68 last_name='doe', 69 sex='M', 70 # age=this_year-year_of_birth, 71 age=calculate_age(year_of_birth), 72 ) 73 self.assertEqual(reality, my_expectation) 74 75 def test_factory_person_says_hello(self):the test is still green.
I remove the commented lines
53 def test_factory_w_optional_arguments(self): 54 first_name = get_random_name() 55 56 this_year = get_current_year() 57 year_of_birth = random.randint( 58 this_year-120, this_year 59 ) 60 61 reality = src.person.factory( 62 first_name=first_name, 63 year_of_birth=year_of_birth, 64 ) 65 my_expectation = dict( 66 first_name=first_name, 67 last_name='doe', 68 sex='M', 69 age=calculate_age(year_of_birth), 70 ) 71 self.assertEqual(reality, my_expectation) 72 73 def test_factory_person_says_hello(self):I add a call to the function for the value of
ageforjoein test_factory_person_says_hello73 def test_factory_person_says_hello(self): 74 # this_year = datetime.datetime.now().year 75 this_year = get_current_year() 76 77 first_name = 'joe' 78 last_name = 'blow' 79 year_of_birth = 1996 80 # age = this_year - year_of_birth 81 age = calculate_age(year_of_birth) 82 83 joe = src.person.factory( 84 first_name=first_name, 85 last_name=last_name, 86 year_of_birth=year_of_birth, 87 ) 88 89 reality = src.person.say_hello(joe) 90 my_expectation = ( 91 f'Hello, my name is {first_name}' 92 f' {last_name} and I am {age}' 93 ) 94 self.assertEqual(reality, my_expectation) 95 96 first_name = 'jane' 97 year_of_birth = 1991 98 age = this_year - year_of_birththe test is still green.
I add a call to the function for the value of
ageforjane96 first_name = 'jane' 97 year_of_birth = 1991 98 # age = this_year - year_of_birth 99 age = calculate_age(year_of_birth) 100 101 jane = src.person.factory( 102 first_name=first_name, 103 sex='F', 104 year_of_birth=year_of_birth 105 ) 106 107 reality = src.person.say_hello(jane) 108 my_expectation = ( 109 f'Hello, my name is {first_name}' 110 f' doe and I am {age}' 111 ) 112 self.assertEqual(reality, my_expectation) 113 114 first_name = 'john' 115 last_name = 'smith' 116 year_of_birth = 1580 117 age = this_year - year_of_birthstill green.
I add a call to the function for the value of
ageforjohn114 first_name = 'john' 115 last_name = 'smith' 116 year_of_birth = 1580 117 # age = this_year - year_of_birth 118 age = calculate_age(year_of_birth) 119 120 john = src.person.factory( 121 first_name=first_name, 122 last_name=last_name, 123 year_of_birth=year_of_birth, 124 ) 125 126 reality = src.person.say_hello(john) 127 my_expectation = ( 128 f'Hello, my name is {first_name}' 129 f' {last_name} and I am {age}' 130 ) 131 self.assertEqual(reality, my_expectation) 132 133 first_name = 'mary' 134 last_name = 'public' 135 year_of_birth = 2000 136 age = this_year - year_of_birthgreen.
I add a call to the function for the value of
ageformary133 first_name = 'mary' 134 last_name = 'public' 135 year_of_birth = 2000 136 # age = this_year - year_of_birth 137 age = calculate_age(year_of_birth)the test is still green.
I add a git commit message in the other terminal
git commit -am 'extract calculate_age function'the terminal shows a summary of the changes then goes back to the command line.
test_factory_person_says_hello with random year_of_birth
I want to use random values for year_of_birth in the tests
I go back to the terminal where the tests are running
I use a call to random.randint to get a random value for the
year_of_birthofjoe73 def test_factory_person_says_hello(self): 74 # this_year = datetime.datetime.now().year 75 this_year = get_current_year() 76 77 first_name = 'joe' 78 last_name = 'blow' 79 # year_of_birth = 1996 80 year_of_birth = random.randint( 81 this_year-120, this_year 82 ) 83 # age = this_year - year_of_birth 84 age = calculate_age(year_of_birth) 85 86 joe = src.person.factory( 87 first_name=first_name, 88 last_name=last_name, 89 year_of_birth=year_of_birth, 90 ) 91 92 reality = src.person.say_hello(joe) 93 my_expectation = ( 94 f'Hello, my name is {first_name}' 95 f' {last_name} and I am {age}' 96 ) 97 self.assertEqual(reality, my_expectation) 98 99 first_name = 'jane' 100 year_of_birth = 1991 101 # age = this_year - year_of_birth 102 age = calculate_age(year_of_birth)the test is still green.
I use a call to random.randint to get a random value for the
year_of_birthofjane99 first_name = 'jane' 100 # year_of_birth = 1991 101 year_of_birth = random.randint( 102 this_year-120, this_year 103 ) 104 # age = this_year - year_of_birth 105 age = calculate_age(year_of_birth) 106 107 jane = src.person.factory( 108 first_name=first_name, 109 sex='F', 110 year_of_birth=year_of_birth 111 ) 112 113 reality = src.person.say_hello(jane) 114 my_expectation = ( 115 f'Hello, my name is {first_name}' 116 f' doe and I am {age}' 117 ) 118 self.assertEqual(reality, my_expectation) 119 120 first_name = 'john' 121 last_name = 'smith' 122 year_of_birth = 1580 123 # age = this_year - year_of_birth 124 age = calculate_age(year_of_birth)still green.
I use a call to random.randint to get a random value for the
year_of_birthofjohn120 first_name = 'john' 121 last_name = 'smith' 122 # year_of_birth = 1580 123 year_of_birth = random.randint( 124 this_year-120, this_year 125 ) 126 # age = this_year - year_of_birth 127 age = calculate_age(year_of_birth) 128 129 john = src.person.factory( 130 first_name=first_name, 131 last_name=last_name, 132 year_of_birth=year_of_birth, 133 ) 134 135 reality = src.person.say_hello(john) 136 my_expectation = ( 137 f'Hello, my name is {first_name}' 138 f' {last_name} and I am {age}' 139 ) 140 self.assertEqual(reality, my_expectation) 141 142 first_name = 'mary' 143 last_name = 'public' 144 year_of_birth = 2000 145 # age = this_year - year_of_birth 146 age = calculate_age(year_of_birth)green.
I use a call to random.randint to get a random value for the
year_of_birthofmary142 first_name = 'mary' 143 last_name = 'public' 144 # year_of_birth = 2000 145 year_of_birth = random.randint( 146 this_year-120, this_year 147 ) 148 # age = this_year - year_of_birth 149 age = calculate_age(year_of_birth) 150 151 mary = src.person.factory( 152 first_name=first_name, 153 last_name=last_name, 154 year_of_birth=year_of_birth, 155 sex='F', 156 ) 157 158 reality = src.person.say_hello(mary) 159 my_expectation = ( 160 f'Hello, my name is {first_name}' 161 f' {last_name} and I am {age}' 162 ) 163 self.assertEqual(reality, my_expectation) 164 165 166# Exceptions seenstill green.
I add a git commit message in the other terminal
git commit -am \ 'test_factory_person_says_hello with random year_of_birth'the terminal shows a summary of the changes then goes back to the command line.
extract get_random_year_of_birth function
I make the this_year and year_of_birth variables the same way in all three tests.
I go back to the terminal where the tests are running
I add a function to use to replace the repetition of making the values for the
this_yearandyear_of_birthvariables intest_person.py22def calculate_age(year_of_birth): 23 return ( 24 get_current_year() 25 - year_of_birth 26 ) 27 28 29def get_random_year_of_birth(): 30 this_year = get_current_year() 31 return random.randint( 32 this_year-120, this_year 33 ) 34 35 36class TestPerson(unittest.TestCase): 37 38 def test_factory_w_keyword_arguments(self):I use the
get_random_year_of_birthfunction to replace the repetition of making the values for thethis_yearandyear_of_birthvariables in test_factory_w_keyword_arguments38 def test_factory_w_keyword_arguments(self): 39 a_person = dict( 40 first_name=get_random_name(), 41 last_name=get_random_name(), 42 sex=pick_one('F', 'M'), 43 ) 44 45 # this_year = get_current_year() 46 # year_of_birth = random.randint( 47 # this_year-120, this_year 48 # ) 49 year_of_birth = get_random_year_of_birth()the test is still green.
I remove the commented lines
38 def test_factory_w_keyword_arguments(self): 39 a_person = dict( 40 first_name=get_random_name(), 41 last_name=get_random_name(), 42 sex=pick_one('F', 'M'), 43 ) 44 year_of_birth = get_random_year_of_birth() 45 46 reality = src.person.factory( 47 **a_person, 48 year_of_birth=year_of_birth, 49 ) 50 my_expectation = dict( 51 a_person, 52 age=calculate_age(year_of_birth), 53 ) 54 self.assertEqual(reality, my_expectation) 55 56 def test_factory_w_optional_arguments(self):I use the
get_random_year_of_birthfunction to replace the repetition of making the values for thethis_yearandyear_of_birthvariables in test_factory_w_optional_arguments56 def test_factory_w_optional_arguments(self): 57 first_name = get_random_name() 58 59 # this_year = get_current_year() 60 # year_of_birth = random.randint( 61 # this_year-120, this_year 62 # ) 63 year_of_birth = get_random_year_of_birth()the test is still green.
I remove the commented lines
56 def test_factory_w_optional_arguments(self): 57 first_name = get_random_name() 58 year_of_birth = get_random_year_of_birth() 59 60 reality = src.person.factory( 61 first_name=first_name, 62 year_of_birth=year_of_birth, 63 ) 64 my_expectation = dict( 65 first_name=first_name, 66 last_name='doe', 67 sex='M', 68 age=calculate_age(year_of_birth), 69 ) 70 self.assertEqual(reality, my_expectation) 71 72 def test_factory_person_says_hello(self):I use the
get_random_year_of_birthfunction to replace the repetition of making the values for thethis_yearandyear_of_birthvariables forjoein test_factory_person_says_hello72 def test_factory_person_says_hello(self): 73 # this_year = datetime.datetime.now().year 74 this_year = get_current_year() 75 76 first_name = 'joe' 77 last_name = 'blow' 78 # year_of_birth = 1996 79 # year_of_birth = random.randint( 80 # this_year-120, this_year 81 # ) 82 # age = this_year - year_of_birth 83 year_of_birth = get_random_year_of_birth() 84 age = calculate_age(year_of_birth) 85 86 joe = src.person.factory( 87 first_name=first_name, 88 last_name=last_name, 89 year_of_birth=year_of_birth, 90 ) 91 92 reality = src.person.say_hello(joe) 93 my_expectation = ( 94 f'Hello, my name is {first_name}' 95 f' {last_name} and I am {age}' 96 ) 97 self.assertEqual(reality, my_expectation) 98 99 first_name = 'jane' 100 # year_of_birth = 1991 101 year_of_birth = random.randint( 102 this_year-120, this_year 103 ) 104 # age = this_year - year_of_birth 105 age = calculate_age(year_of_birth)the test is still green.
I use the
get_random_year_of_birthfunction to replace the repetition of making the values for thethis_yearandyear_of_birthvariables forjane99 first_name = 'jane' 100 # year_of_birth = 1991 101 # year_of_birth = random.randint( 102 # this_year-120, this_year 103 # ) 104 # age = this_year - year_of_birth 105 year_of_birth = get_random_year_of_birth() 106 age = calculate_age(year_of_birth) 107 108 jane = src.person.factory( 109 first_name=first_name, 110 sex='F', 111 year_of_birth=year_of_birth 112 ) 113 114 reality = src.person.say_hello(jane) 115 my_expectation = ( 116 f'Hello, my name is {first_name}' 117 f' doe and I am {age}' 118 ) 119 self.assertEqual(reality, my_expectation) 120 121 first_name = 'john' 122 last_name = 'smith' 123 # year_of_birth = 1580 124 year_of_birth = random.randint( 125 this_year-120, this_year 126 ) 127 # age = this_year - year_of_birth 128 age = calculate_age(year_of_birth)still green.
I use the
get_random_year_of_birthfunction to replace the repetition of making the values for thethis_yearandyear_of_birthvariables forjohn121 first_name = 'john' 122 last_name = 'smith' 123 # year_of_birth = 1580 124 # year_of_birth = random.randint( 125 # this_year-120, this_year 126 # ) 127 # age = this_year - year_of_birth 128 year_of_birth = get_random_year_of_birth() 129 age = calculate_age(year_of_birth) 130 131 john = src.person.factory( 132 first_name=first_name, 133 last_name=last_name, 134 year_of_birth=year_of_birth, 135 ) 136 137 reality = src.person.say_hello(john) 138 my_expectation = ( 139 f'Hello, my name is {first_name}' 140 f' {last_name} and I am {age}' 141 ) 142 self.assertEqual(reality, my_expectation) 143 144 first_name = 'mary' 145 last_name = 'public' 146 # year_of_birth = 2000 147 year_of_birth = random.randint( 148 this_year-120, this_year 149 ) 150 # age = this_year - year_of_birth 151 age = calculate_age(year_of_birth)green.
I use the
get_random_year_of_birthfunction to replace the repetition of making the values for thethis_yearandyear_of_birthvariables formary144 first_name = 'mary' 145 last_name = 'public' 146 # year_of_birth = 2000 147 # year_of_birth = random.randint( 148 # this_year-120, this_year 149 # ) 150 # age = this_year - year_of_birth 151 year_of_birth = get_random_year_of_birth() 152 age = calculate_age(year_of_birth)still green.
I remove the commented lines and
this_yearvariable since it is no longer used72 def test_factory_person_says_hello(self): 73 first_name = 'joe' 74 last_name = 'blow' 75 year_of_birth = get_random_year_of_birth() 76 age = calculate_age(year_of_birth) 77 78 joe = src.person.factory( 79 first_name=first_name, 80 last_name=last_name, 81 year_of_birth=year_of_birth, 82 ) 83 84 reality = src.person.say_hello(joe) 85 my_expectation = ( 86 f'Hello, my name is {first_name}' 87 f' {last_name} and I am {age}' 88 ) 89 self.assertEqual(reality, my_expectation) 90 91 first_name = 'jane' 92 year_of_birth = get_random_year_of_birth() 93 age = calculate_age(year_of_birth) 94 95 jane = src.person.factory( 96 first_name=first_name, 97 sex='F', 98 year_of_birth=year_of_birth 99 ) 100 101 reality = src.person.say_hello(jane) 102 my_expectation = ( 103 f'Hello, my name is {first_name}' 104 f' doe and I am {age}' 105 ) 106 self.assertEqual(reality, my_expectation) 107 108 first_name = 'john' 109 last_name = 'smith' 110 year_of_birth = get_random_year_of_birth() 111 age = calculate_age(year_of_birth) 112 113 john = src.person.factory( 114 first_name=first_name, 115 last_name=last_name, 116 year_of_birth=year_of_birth, 117 ) 118 119 reality = src.person.say_hello(john) 120 my_expectation = ( 121 f'Hello, my name is {first_name}' 122 f' {last_name} and I am {age}' 123 ) 124 self.assertEqual(reality, my_expectation) 125 126 first_name = 'mary' 127 last_name = 'public' 128 year_of_birth = get_random_year_of_birth() 129 age = calculate_age(year_of_birth) 130 131 mary = src.person.factory( 132 first_name=first_name, 133 last_name=last_name, 134 year_of_birth=year_of_birth, 135 sex='F', 136 ) 137 138 reality = src.person.say_hello(mary) 139 my_expectation = ( 140 f'Hello, my name is {first_name}' 141 f' {last_name} and I am {age}' 142 ) 143 self.assertEqual(reality, my_expectation) 144 145 146# Exceptions seen 147# AssertionError 148# NameError 149# AttributeError 150# TypeError 151# SyntaxErrorI add a git commit message in the other terminal
git commit -am \ 'extract get_random_year_of_birth function'the terminal shows a summary of the changes then goes back to the command line.
test_person_tests
I want to write the solution without looking at the tests
RED: make it fail
I go back to the terminal where the tests are running
I close
test_person.pythen I delete all the text in
person.py, the terminal is my friend, and shows AttributeErrorFAILED ...test_factory_person_says_hello - AttributeError: module 'src.person' has no attribute 'factory' FAILED ...test_factory_w_keyword_arguments - AttributeError: module 'src.person' has no attribute 'factory' FAILED ...test_factory_w_optional_arguments - AttributeError: module 'src.person' has no attribute 'factory'because there is nothing in
person.pywith the namefactory.
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
1factorythe terminal is my friend, and shows NameError
NameError: name 'factory' is not definedbecause I have not told Python what
factorymeansI point it to None to define it
1# factory 2factory = Nonethe terminal is my friend, and shows TypeError
TypeError: 'NoneType' object is not callablebecause
factorypoints to None and I cannot call None like a function.I make
factorya function to make it callable1# factory 2# factory = None 3def factory(): 4 return Nonethe terminal is my friend, and shows TypeError
TypeError: factory() got an unexpected keyword argument 'first_name'because the test called the factory function with a name (
first_name) that is not in the function definitionI add
first_nameto the function definition1# factory 2# factory = None 3# def factory(): 4def factory(first_name): 5 return Nonethe terminal is my friend, and shows TypeError
TypeError: factory() got an unexpected keyword argument 'year_of_birth'because the test called the factory function with a name (
year_of_birth) that is not in the function definitionI add
year_of_birthto the function definition1# factory 2# factory = None 3# def factory(): 4# def factory(first_name): 5def factory(first_name, year_of_birth): 6 return Nonethe terminal is my friend, and shows AssertionError
AssertionError: None != {'first_name': Y, 'last_name': 'doe', 'sex': 'M', 'age': X}because the assertion expects a dictionary and the factory function returns None
I copy and paste the dictionary from the terminal to make the function return a dictionary instead of None
1# factory 2# factory = None 3# def factory(): 4# def factory(first_name): 5def factory(first_name, year_of_birth): 6 # return None 7 return { 8 'first_name': 'john', 9 'last_name': 'doe', 10 'sex': 'M', 11 'age': 55, 12 }I use ctrl/command+s (Windows & Linux/MacOS) to run the test a few times and the terminal shows AssertionError
AssertionError: {'first_name': A, 'last_name': 'doe', 'sex': 'M', 'age': Y} != {'first_name': Z, 'last_name': 'doe', 'sex': 'M', 'age': X}I use the
first_nameinput parameter in the return statement1# factory 2# factory = None 3# def factory(): 4# def factory(first_name): 5def factory(first_name, year_of_birth): 6 # return None 7 return { 8 # 'first_name': 'john', 9 'first_name': first_name, 10 'last_name': 'doe', 11 'sex': 'M', 12 'age': 55, 13 }the first name matches and if the ages are different, the terminal shows AssertionError
AssertionError: {'first_name': Z, 'last_name': 'doe', 'sex': 'M', 'age': Y} != {'first_name': Z, 'last_name': 'doe', 'sex': 'M', 'age': X}and TypeError
TypeError: factory() got an unexpected keyword argument 'last_name'. Did you mean 'first_name'?because the call uses a name (
last_name) that is not in the parentheses of its definition.I use the
year_of_birthinput parameter in the return statement for the value ofage1# factory 2# factory = None 3# def factory(): 4# def factory(first_name): 5def factory(first_name, year_of_birth): 6 # return None 7 return { 8 # 'first_name': 'john', 9 'first_name': first_name, 10 'last_name': 'doe', 11 'sex': 'M', 12 # 'age': 55, 13 'age': year_of_birth, 14 }the terminal is my friend, and shows AssertionError
AssertionError: {'first_name': Y, 'last_name': 'doe', 'sex': 'M', 'age': ABCD} != {'first_name': Y, 'last_name': 'doe', 'sex': 'M', 'age': X}because the factory function returned 4 digits (a year) as the value for the
agekey, and the assertion expects the difference between that value and the current yearI add an import statement for the `datetime module`_ at the top of the file
1import datetime 2 3 4 5# factory 6# factory = None 7# def factory(): 8# def factory(first_name): 9def factory(first_name, year_of_birth):the terminal still shows AssertionError
I use the `datetime module`_ to get the current year, then use it for the
agecalculation9def factory(first_name, year_of_birth): 10 # return None 11 return { 12 # 'first_name': 'john', 13 'first_name': first_name, 14 'last_name': 'doe', 15 'sex': 'M', 16 # 'age': 55, 17 # 'age': year_of_birth, 18 'age': ( 19 datetime.datetime.today().year 20 - year_of_birth 21 ), 22 }the terminal is my friend, and shows TypeError
TypeError: factory() got an unexpected keyword argument 'last_name'. Did you mean 'first_name'?because the test called the factory function with a keyword argument (
last_name) that is not in the function definitionI add a new input parameter to the function
9# def factory(first_name, year_of_birth): 10def factory( 11 first_name, year_of_birth, 12 last_name, 13 ):the terminal is my friend, and shows TypeError
TypeError: factory() missing 1 required positional argument: 'last_name'because the test called the function with another argument and Python took that argument as a positional argument for
last_nameI add a default value for
last_nameso Python does not take it is a positional argument when a name is not given10def factory( 11 first_name, year_of_birth, 12 # last_name, 13 last_name=None, 14 ):the terminal is my friend, and shows TypeError
TypeError: factory() got an unexpected keyword argument 'sex'because the test called the factory function with a keyword argument (
sex) that is not in the function definitionI add the name to the definition of the function
10def factory( 11 first_name, year_of_birth, 12 # last_name, 13 # last_name=None, 14 last_name=None, sex, 15 ):the terminal is my friend, and shows SyntaxError
SyntaxError: parameter without a default follows parameter with a defaultbecause parameters without default values must come before parameters with default values.
I add a default value for
sexto make it optional10def factory( 11 first_name, year_of_birth, 12 # last_name, 13 # last_name=None, 14 # last_name=None, sex, 15 last_name=None, sex=None, 16 ):the terminal is my friend, and shows AssertionError
first_nameandagematch. If the last names are different, the terminal is my friend, and shows AssertionErrorAssertionError: {'first_name': C, 'last_name': B, 'sex': Z, 'age': X} != {'first_name': C, 'last_name': A, 'sex': Y, 'age': X}and AttributeError
AttributeError: module 'src.person' has no attribute 'say_hello'I use the
sexinput parameter in the return statement10def factory( 11 first_name, year_of_birth, 12 # last_name, 13 # last_name=None, 14 # last_name=None, sex, 15 last_name=None, sex=None, 16 ): 17 # return None 18 return { 19 # 'first_name': 'john', 20 'first_name': first_name, 21 'last_name': 'doe', 22 # 'sex': 'M', 23 'sex': sex, 24 # 'age': 55, 25 # 'age': year_of_birth, 26 'age': ( 27 datetime.datetime.today().year 28 - year_of_birth 29 ), 30 }the terminal is my friend, and shows AssertionError
AssertionError: {'first_name': Y, 'last_name': 'doe', 'sex': None, 'age': X} != {'first_name': Y, 'last_name': 'doe', 'sex': 'M', 'age': X}because the assertion expects
'M'as the value ofsexand the function returns None which is its default valueI change the default value of
sexto'M'10def factory( 11 first_name, year_of_birth, 12 # last_name, 13 # last_name=None, 14 # last_name=None, sex, 15 # last_name=None, sex=None, 16 last_name=None, sex='M', 17):I use ctrl/command+s (Windows & Linux/MacOS) to run the test a few times and if the
last_nameis not'doe', the terminal is my friend, and shows AssertionErrorAssertionError: {'first_name': A, 'last_name': 'doe', 'sex': Y, 'age': X} != {'first_name': A, 'last_name': Z, 'sex': Y, 'age': X}because the
last_namevalue is different between the two dictionaries. It still shows AttributeErrorAttributeError: module 'src.person' has no attribute 'say_hello'I use the
last_nameinput parameter in the return statement4def factory( 5 first_name, year_of_birth, 6 # last_name, 7 # last_name=None, 8 # last_name=None, sex, 9 # last_name=None, sex=None, 10 last_name=None, sex='M', 11 ): 12 # return None 13 return { 14 # 'first_name': 'john', 15 'first_name': first_name, 16 # 'last_name': 'doe', 17 'last_name': last_name, 18 # 'sex': 'M', 19 'sex': sex, 20 # 'age': 55, 21 # 'age': year_of_birth, 22 'age': ( 23 datetime.datetime.today().year 24 - year_of_birth 25 ), 26 }the terminal is my friend, and shows AssertionError
AssertionError: {'first_name': Z, 'last_name': None, 'sex': Y, 'age': X} != {'first_name': Z, 'last_name': 'doe', 'sex': Y, 'age': X}because the assertion expects
'doe'as the value oflast_nameand the function returns None which is its default valueI change the default value for
last_nameto match the expectation10def factory( 11 first_name, year_of_birth, 12 # last_name, 13 # last_name=None, 14 # last_name=None, sex, 15 # last_name=None, sex=None, 16 # last_name=None, sex='M', 17 last_name='doe', sex='M', 18 ):the terminal is my friend, and shows AttributeError
AttributeError: module 'src.person' has no attribute 'say_hello'because I do not have a definition for
say_helloI add
say_hello1import datetime 2 3 4say_hello 5 6 7# factory 8# factory = None 9# def factory(): 10# def factory(first_name): 11# def factory(first_name, year_of_birth): 12def factory(the terminal is my friend, and shows NameError
NameError: name 'say_hello' is not definedI point it to None to define it
1import datetime 2 3 4# say_hello 5say_hello = None 6 7 8# factorythe 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 callable1import datetime 2 3 4# say_hello 5# say_hello = None 6def say_hello(): 7 return None 8 9 10# factorythe terminal is my friend, and shows TypeError
TypeError: say_hello() takes 0 positional arguments but 1 was givenbecause the function definition for
say_hellodoes not allow calling it with inputs (the parentheses are empty) and the test sends input.I add a name to the function definition
4# say_hello 5# say_hello = None 6# def say_hello(): 7def say_hello(argument): 8 return Nonethe terminal is my friend, and shows AssertionError
AssertionError: None != 'Hello, my name is Z Y and I am Z'because the test expects a string and the
say_hellofunction returns NoneI copy (ctrl/command+c) the string from the terminal and paste it (ctrl/command+v) to replace the return statement
4# say_hello 5# say_hello = None 6# def say_hello(): 7def say_hello(argument): 8 # return None 9 return 'Hello, my name is jade doe and I am 66' 10 11 12# factoryI use ctrl/command+s (Windows & Linux/MacOS) to run the test a few times, and the terminal shows AssertionError
AssertionError: 'Hello, my name is Z Y and I am X' != 'Hello, my name is A B and I am C'the names and ages change
I return the input to compare it with what the test expects
4# say_hello 5# say_hello = None 6# def say_hello(): 7def say_hello(argument): 8 # return None 9 return argument 10 return 'Hello, my name is jade doe and I am 66'the terminal shows AssertionError
AssertionError: {'first_name': A, 'last_name': Z, 'sex': Y, 'age': X} != 'Hello, my name is A Z and I am X'the test sends a dictionary as input and expects a string as output, and the string uses the values of the
first_name,last_nameandagekeys from the dictionary it receivesI return an f-string with the values of the
first_name,last_nameandagekeys from the dictionary4# say_hello 5# say_hello = None 6# def say_hello(): 7def say_hello(argument): 8 # return None 9 return ( 10 f'Hello, my name is {argument.get("first_name")}' 11 f' {argument.get("last_name")}' 12 f' and I am {argument.get("age")}' 13 ) 14 return argument 15 return 'Hello, my name is jade doe and I am 66'the test passes. Okay!
REFACTOR: make it better
I use the
Rename Symbolfeature of the Integrated Development Environment (IDE) to changeargumentto make it clearer4# say_hello 5# say_hello = None 6# def say_hello(): 7def say_hello(a_dictionary): 8 # return None 9 return ( 10 f'Hello, my name is {a_dictionary.get("first_name")}' 11 f' {a_dictionary.get("last_name")}' 12 f' and I am {a_dictionary.get("age")}' 13 ) 14 return a_dictionary 15 return 'Hello, my name is jade doe and I am 66'I remove the commented lines and other return statements
1import datetime 2 3 4def say_hello(a_dictionary): 5 return ( 6 f'Hello, my name is {a_dictionary.get("first_name")}' 7 f' {a_dictionary.get("last_name")}' 8 f' and I am {a_dictionary.get("age")}' 9 ) 10 11 12def factory( 13 first_name, year_of_birth, 14 last_name='doe', sex='M', 15 ): 16 return { 17 'first_name': first_name, 18 'last_name': last_name, 19 'sex': sex, 20 'age': ( 21 datetime.datetime.today().year 22 - year_of_birth 23 ), 24 }This factory function only has two parameters with default values (
last_nameandsex)def factory( first_name, year_of_birth, last_name='doe', sex='M', ):the first solution had three parameters with default values (
last_name,sexandyear_of_birth)def factory( first_name, last_name='doe', sex='M', year_of_birth=None, ):I can learn new things from repetition.
I add a git commit message in the other terminal
git commit -am \ 'refactor factory and say_hello functions'the terminal shows a summary of the changes then goes back to the command line.
close the project
I close
person.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 make
a function that takes in keyword arguments as input, has default values for some of them, performs an action based on an input and returns a dictionary as output
a function that takes in a dictionary and returns a string as output with values of keys from the dictionary
I also saw these Exceptions
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.