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'Hi, 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'Hi, 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'Hi, 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'Hi, 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_.pyif you ran
echo "pytest-watcher" >> requirements.txt, to addpytest-watcherto the requirements file
fix those errors and try to run
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 function definition for
factoryinperson.pyin thesrcfolder, does not allow calling it with inputs (the parentheses are empty) and the test sends'first_name'as input.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 keyword argument (
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 keyword argument (
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 keyword argument (
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 module7 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 objectdatetime.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 'Hi, 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 != 'Hi, 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 'Hi, 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 'Hi, 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 'Hi, 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: 'Hi, my name is joe blow and I am 30' != 'Hi, 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 'Hi, my name is joe blow and I am 30' 24 return ( 25 f'Hi, my name is {first_name}' 26 ' blow and I am 30' 27 )the terminal is my friend, and shows AssertionError
AssertionError: 'Hi, my name is jane blow and I am 30' != 'Hi, 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'Hi, my name is {first_name}' ' blow and I am 30' )
src.person.say_hello(jane) # the say_hello function returns 'Hi, 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 'Hi, my name is joe blow and I am 30' 25 return ( 26 # f'Hi, my name is {first_name}' 27 # ' blow and I am 30' 28 f'Hi, my name is {first_name} {last_name}' 29 ' and I am 30' 30 )the terminal is my friend, and shows AssertionError
AssertionError: 'Hi, my name is jane doe and I am 30' != 'Hi, 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'Hi, my name is {first_name} {last_name}' ' and I am 30' )
src.person.say_hello(jane) # the say_hello function returns 'Hi, 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 'Hi, my name is joe blow and I am 30' 26 return ( 27 # f'Hi, my name is {first_name}' 28 # ' blow and I am 30' 29 f'Hi, 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('last_name') jane.get('last_name') { 'first_name': 'jane', 'last_name': 'doe', 'sex': 'F', 'age': 35 }.get('age') # the get method returns 35return ( f'Hi, my name is {first_name} {last_name}' f' and I am {age}' )
src.person.say_hello(jane) # the say_hello function returns 'Hi, 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'Hi, 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 'Hi, 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 'Hi, 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: 'Hi, my name is john smith and I am 446' != 'Hi, 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 # 'Hi, my name is jane doe and I am' 99 # f' {datetime.datetime.now().year-1991}' 100 'Hi, 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 # 'Hi, my name is jane doe and I am' 99 # f' {datetime.datetime.now().year-1991}' 100 'Hi, 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 'Hi, 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: 'Hi, my name is mary public and I am 26' != 'Hi, 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 # 'Hi, my name is john smith and I am' 115 # f' {datetime.datetime.now().year-1580}' 116 'Hi, 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 'Hi, 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 'Hi, 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 # 'Hi, my name is jane doe and I am' 103 # f' {datetime.datetime.now().year-1991}' 104 'Hi, 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 'Hi, 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 # 'Hi, my name is jane doe and I am' 103 # f' {datetime.datetime.now().year-1991}' 104 'Hi, 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 # 'Hi, my name is mary public and I am' 124 # f' {datetime.datetime.now().year-2000}' 125 # f' {this_year-2000}' 126 f'Hi, 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 'Hi, 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 # 'Hi, my name is jane doe and I am' 111 # f' {datetime.datetime.now().year-1991}' 112 # 'Hi, my name is john smith and I am' 113 # f' {datetime.datetime.now().year-1580}' 114 # f' {this_year-1580}' 115 f'Hi, 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 'Hi, 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 # 'Hi, my name is jane doe and I am' 95 # f' {datetime.datetime.now().year-1991}' 96 # f' {this_year-1991}' 97 f'Hi, 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 # 'Hi, my name is joe blow and I am' 83 # f' {datetime.datetime.now().year-1996}' 84 # f' {this_year-1996}' 85 f'Hi, 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'Hi, 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'Hi, 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'Hi, 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'Hi, 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 has 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'Hi, 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'Hi, 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'Hi, 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'Hi, 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'Hi, 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'Hi, 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'Hi, 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'Hi, 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'Hi, 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'Hi, 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'Hi, 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'Hi, 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'Hi, 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'Hi, 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 keyword argument (
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 keyword argument (
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'?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 != 'Hi, 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 'Hi, 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: 'Hi, my name is Z Y and I am X' != 'Hi, 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 'Hi, my name is jade doe and I am 66'the terminal shows AssertionError
AssertionError: {'first_name': A, 'last_name': Z, 'sex': Y, 'age': X} != 'Hi, 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'Hi, 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 'Hi, 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'Hi, 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 'Hi, 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'Hi, 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?
you know:
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.