how to make a person
This is an exercise in making dictionaries with functions, two important objects to know in Python.
Imagine 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 random
3import src.person
4import unittest
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 calculate_age(year_of_birth):
19 return (
20 datetime.datetime.now().year
21 - year_of_birth
22 )
23
24
25def get_random_year_of_birth():
26 this_year = datetime.datetime.now().year
27 return random.randint(
28 this_year-120, this_year
29 )
30
31
32class TestPerson(unittest.TestCase):
33
34 def test_factory_w_keyword_arguments(self):
35 year_of_birth = get_random_year_of_birth()
36
37 a_person = dict(
38 first_name=get_random_name(),
39 last_name=get_random_name(),
40 sex=pick_one('F', 'M'),
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):
54 first_name = get_random_name()
55 year_of_birth = get_random_year_of_birth()
56
57 reality = src.person.factory(
58 first_name=first_name,
59 year_of_birth=year_of_birth,
60 )
61 my_expectation = dict(
62 first_name=first_name,
63 last_name='doe',
64 sex='M',
65 age=calculate_age(year_of_birth),
66 )
67 self.assertEqual(reality, my_expectation)
68
69 def test_factory_person_says_hello(self):
70 first_name = get_random_name()
71 last_name = get_random_name()
72 sex = pick_one('F', 'M')
73
74 year_of_birth = get_random_year_of_birth()
75 age = calculate_age(year_of_birth)
76
77 a_random_person = src.person.factory(
78 first_name=first_name,
79 last_name=last_name,
80 sex=sex,
81 year_of_birth=year_of_birth,
82 )
83
84 reality = src.person.say_hello(a_random_person)
85 my_expectation = (
86 f'Hi, my name is {first_name} {last_name}'
87 f' and I am {age}'
88 )
89 self.assertEqual(reality, my_expectation)
90
91
92# Exceptions seen
93# AssertionError
94# NameError
95# AttributeError
96# TypeError
97# 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 make a directory for the source code
mkdir 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-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, 148 insertions(+) create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 README.md create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 src/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# AssertionErrorthen I 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.pywith that name
I add AttributeError to the list of Exceptions seen
13# Exceptions seen 14# AssertionError 15# NameError 16# AttributeErrorI use the Explorer to open
person.pyfrom thesrcfolderI delete the text, then add a function to
person.py1def factory(): 2 return Nonethe test passes.
REFACTOR: make it better
I want the function to take a keyword argument called
first_name. I add it to the test intest_person.py7 def test_factory_w_keyword_arguments(self): 8 reality = src.person.factory( 9 first_name='first_name', 10 ) 11 my_expectation = None 12 self.assertEqual(reality, my_expectation) 13 14 15# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: factory() got an unexpected keyword argument 'first_name'because the function definition for
src.person.factorydoes 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
15# Exceptions seen 16# AssertionError 17# NameError 18# AttributeError 19# TypeErrorI add
first_nameas an input parameter to the function inperson.py1def factory(first_name): 2 return Nonethe test passes.
I want the function to take a keyword 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 first_name='first_name', 10 last_name='last_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 '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
last_nameto the function definition inperson.py1def factory(first_name, last_name): 2 return Nonethe test passes.
I want the function to take a keyword 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 first_name='first_name', 10 last_name='last_name', 11 sex='M', 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 'sex'because the test called the factory function with a keyword argument (
sex) that is not in the function definitionI add
sexas an input parameter to the factory function inperson.py1def factory( 2 first_name, last_name, 3 sex, 4 ): 5 return Nonethe test passes.
I want the function to take a keyword 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 first_name='first_name', 10 last_name='last_name', 11 sex='M', 12 year_of_birth=2000, 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 '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 the name to the function definition in
person.py1def factory( 2 first_name, last_name, 3 sex, year_of_birth, 4 ): 5 return Nonethe test passes.
I want the factory function to return a dictionary (any key-value pairs in curly braces
{ }separated by a comma) as output when it is called. I changemy_expectationintest_person.py7 def test_factory_w_keyword_arguments(self): 8 reality = src.person.factory( 9 first_name='first_name', 10 last_name='last_name', 11 sex='M', 12 year_of_birth=2000, 13 ) 14 my_expectation = dict() 15 self.assertEqual(reality, my_expectation) 16 17 18# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: None != {}because the function returns None and the assertion expects
{}I change None to a dictionary in the return statement in
person.py1def factory( 2 first_name, last_name, 3 sex, year_of_birth, 4 ): 5 # return None 6 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 first_name='first_name', 10 last_name='last_name', 11 sex='M', 12 year_of_birth=2000, 13 ) 14 my_expectation = dict( 15 first_name='first_name', 16 ) 17 self.assertEqual(reality, my_expectation) 18 19 20# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {} != {'first_name': 'first_name'}because the function returns the empty dictionary and the assertion expects one with
first_nameas the keyI change the return statement to give the test what it wants, in
person.py1def factory( 2 first_name, last_name, 3 sex, year_of_birth, 4 ): 5 # return None 6 # return {} 7 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 # first_name='first_name', 10 first_name='jane', 11 last_name='last_name', 12 sex='M', 13 year_of_birth=2000, 14 ) 15 my_expectation = dict( 16 # first_name='first_name', 17 first_name='jane', 18 ) 19 self.assertEqual(reality, my_expectation) 20 21 22# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': 'first_name'} != {'first_name': 'jane'}because I changed the value for
first_nameinmy_expectationI change the value for the
first_namekey inperson.py1def factory( 2 first_name, last_name, 3 sex, year_of_birth, 4 ): 5 # return None 6 # return {} 7 # return {'first_name': 'first_name'} 8 return {'first_name': 'jane'}the test passes.
I typed the value for
first_nametwo times in the test, which means I have to make a change in two places every time I want a different value for it. I add a variable to use to remove the repetition of'jane'fromtest_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'7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 10 reality = src.person.factory( 11 # first_name='first_name', 12 # first_name='jane', 13 first_name=first_name, 14 last_name='last_name', 15 sex='M', 16 year_of_birth=2000, 17 ) 18 my_expectation = dict( 19 # first_name='first_name', 20 # first_name='jane', 21 first_name=first_name, 22 ) 23 self.assertEqual(reality, my_expectation) 24 25 26# 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, 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 # first_name='first_name', 12 # first_name='jane', 13 first_name=first_name, 14 last_name='last_name', 15 sex='M', 16 year_of_birth=2000, 17 ) 18 my_expectation = dict( 19 # first_name='first_name', 20 # first_name='jane', 21 first_name=first_name, 22 last_name='last_name', 23 ) 24 self.assertEqual(reality, my_expectation) 25 26 27# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': 'jane'} != {'first_name': 'jane', 'last_name': 'last_name'}I change the return statement in
person.py1def factory( 2 first_name, last_name, 3 sex, year_of_birth, 4 ): 5 # return None 6 # return {} 7 # return {'first_name': 'first_name'} 8 # return {'first_name': 'jane'} 9 return { 10 'first_name': 'jane', 11 'last_name': 'last_name', 12 }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 # first_name='first_name', 12 # first_name='jane', 13 first_name=first_name, 14 # last_name='last_name', 15 last_name='doe', 16 sex='M', 17 year_of_birth=2000, 18 ) 19 my_expectation = dict( 20 # first_name='first_name', 21 # first_name='jane', 22 first_name=first_name, 23 # last_name='last_name', 24 last_name='doe', 25 ) 26 self.assertEqual(reality, my_expectation) 27 28 29# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'first_name': 'jane', 'last_name': 'last_name'} != {'first_name': 'jane', 'last_name': 'doe'}because I changed the value for
last_nameinmy_expectationI change the value for the
last_namekey inperson.py1def factory( 2 first_name, last_name, 3 sex, year_of_birth, 4 ): 5 # return None 6 # return {} 7 # return {'first_name': 'first_name'} 8 # return {'first_name': 'jane'} 9 return { 10 'first_name': 'jane', 11 # 'last_name': 'last_name', 12 'last_name': 'doe', 13 }the test passes.
I typed the value for
last_nametwo times in the test, which means I have to make a change in two places every time I want a different value for it. I add a variable to use to remove the repetition of'doe'fromtest_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'7 def test_factory_w_keyword_arguments(self): 8 first_name = 'jane' 9 last_name = 'doe' 10 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 last_name=last_name, 18 sex='M', 19 year_of_birth=2000, 20 ) 21 my_expectation = dict( 22 # first_name='first_name', 23 # first_name='jane', 24 first_name=first_name, 25 # last_name='last_name', 26 # last_name='doe', 27 last_name=last_name, 28 ) 29 self.assertEqual(reality, my_expectation) 30 31 32# 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
sexto the dictionary, 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 # first_name='first_name', 13 # first_name='jane', 14 first_name=first_name, 15 # last_name='last_name', 16 # last_name='doe', 17 last_name=last_name, 18 sex='M', 19 year_of_birth=2000, 20 ) 21 my_expectation = dict( 22 # first_name='first_name', 23 # first_name='jane', 24 first_name=first_name, 25 # last_name='last_name', 26 # last_name='doe', 27 last_name=last_name, 28 sex='M', 29 ) 30 self.assertEqual(reality, my_expectation) 31 32 33# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: {'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.py1def factory( 2 first_name, last_name, 3 sex, year_of_birth, 4 ): 5 # return None 6 # return {} 7 # return {'first_name': 'first_name'} 8 # return {'first_name': 'jane'} 9 return { 10 'first_name': 'jane', 11 # 'last_name': 'last_name', 12 'last_name': 'doe', 13 'sex': 'M', 14 }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 # first_name='first_name', 13 # first_name='jane', 14 first_name=first_name, 15 # last_name='last_name', 16 # last_name='doe', 17 last_name=last_name, 18 # sex='M', 19 sex='F', 20 year_of_birth=2000, 21 ) 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 last_name=last_name, 29 # sex='M', 30 sex='F', 31 ) 32 self.assertEqual(reality, my_expectation) 33 34 35# 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 I changed the value for
sexinmy_expectationI change the value for the
sexkey inperson.py1def factory( 2 first_name, last_name, 3 sex, year_of_birth, 4 ): 5 # return None 6 # return {} 7 # return {'first_name': 'first_name'} 8 # return {'first_name': 'jane'} 9 return { 10 'first_name': 'jane', 11 # 'last_name': 'last_name', 12 'last_name': 'doe', 13 # 'sex': 'M', 14 'sex': 'F', 15 }the test passes.
I typed the value for
sextwo times in the test, which means I have to make a change in two places every time I want a different value for it. I add a variable to use to remove the repetition of'F'fromtest_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 # 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 sex=sex, 22 year_of_birth=2000, 23 ) 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 # sex='F', 33 sex=sex, 34 ) 35 self.assertEqual(reality, my_expectation) 36 37 38# 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 # 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 sex=sex, 22 year_of_birth=2000, 23 ) 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 # sex='F', 33 sex=sex, 34 age=2026-2000, 35 ) 36 self.assertEqual(reality, my_expectation) 37 38 39# 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}I add a new key to the return statement in
person.py1def factory( 2 first_name, last_name, 3 sex, year_of_birth, 4 ): 5 # return None 6 # return {} 7 # return {'first_name': 'first_name'} 8 # return {'first_name': 'jane'} 9 return { 10 'first_name': 'jane', 11 # 'last_name': 'last_name', 12 'last_name': 'doe', 13 # 'sex': 'M', 14 'sex': 'F', 15 'age': 26, 16 }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 # 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 sex=sex, 22 # year_of_birth=2000, 23 year_of_birth=1996, 24 ) 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 sex=sex, 35 # age=2026-2000, 36 age=2026-1996, 37 ) 38 self.assertEqual(reality, my_expectation) 39 40 41# 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 I changed
2000inmy_expectationI change the value for the
agekey inperson.py1def factory( 2 first_name, last_name, 3 sex, year_of_birth, 4 ): 5 # return None 6 # return {} 7 # return {'first_name': 'first_name'} 8 # return {'first_name': 'jane'} 9 return { 10 'first_name': 'jane', 11 # 'last_name': 'last_name', 12 'last_name': 'doe', 13 # 'sex': 'M', 14 'sex': 'F', 15 # 'age': 26, 16 'age': 30, 17 }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 have to make a change in two places every time I want a different value for it. I add a variable to use to remove the repetition of the year of birth from
test_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 the year of birth
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 # 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 year_of_birth=year_of_birth, 26 ) 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 # age=2026-1996, 39 age=2026-year_of_birth, 40 ) 41 self.assertEqual(reality, my_expectation) 42 43 44# Exceptions seenthe test is still green.
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 now only need to change the value of
sexin one place in the test.I 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 random year_of_birth
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 that is running the tests
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'?I can also use the today method to get the same value
datetime.datetime.today().yeardatetimeis the datetime moduledatetime.datetimeis a call 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.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.today()is a call to the today method of the datetime.datetime object from the datetime module, it returns a datetime.datetime objectdatetime.datetime.now().yearordatetime.datetime.today().yearasks for the value of theyearclass attribute of the datetime.datetime object returned by the now method or today method of the datetime.datetime object from the datetime module
that was a lot of words, they become clearer in the chapters on classes.
GREEN: make it pass
I add an import statement for the datetime module at the top of test_person.py
1import datetime
2import src.person
3import unittest
the test passes.
import datetime brings in an object (everything in Python is an object) for the datetime module so I can use it in test_person.py.
REFACTOR: make it better
I want to use random values in the test to make sure the factory function can handle different values and always calculates the right age.
I use a random integer (a whole number without decimals) for the
year_of_birthvariable9 def test_factory_w_keyword_arguments(self): 10 first_name = 'jane' 11 last_name = 'doe' 12 sex = 'F' 13 # year_of_birth = 1996 14 year_of_birth = random.randint( 15 datetime.datetime.now().year-120, 16 datetime.datetime.now().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 )the terminal is my friend, and shows NameError
NameError: name 'random' is not defined. Did you forget to import 'random'?randomis the random modulerandom.randint()is a call to the randint method from the random module. Okay, this one does not use the same name againdatetime.datetime.now().yeargives me this yeardatetime.datetime.now().year-120gives me this year minus120random.randint(datetime.datetime.now().year-120, datetime.datetime.now().year)gives me a random number from 120 years ago, up to and including the current year
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 agerandom is a module from The Python Standard Library that is used to make fake random numbers
import randombrings in an object (everything in Python is an object) for the random module so I can use it intest_person.pyI like to sort my import statements alphabetically
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.
import datetimebrings in an object (everything in Python is an object) for the datetime module so I can use it inperson.py.I add a variable
9 def test_factory_w_keyword_arguments(self): 10 first_name = 'jane' 11 last_name = 'doe' 12 sex = 'F' 13 # year_of_birth = 1996 14 this_year = datetime.datetime.now().year 15 year_of_birth = random.randint( 16 datetime.datetime.now().year-120, 17 datetime.datetime.now().year 18 )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 # year_of_birth = 1996 14 this_year = datetime.datetime.now().year 15 year_of_birth = random.randint( 16 # datetime.datetime.now().year-120, 17 # datetime.datetime.now().year 18 this_year-120, this_year 19 ) 20 21 reality = src.person.factory( 22 first_name=first_name, 23 last_name=last_name, 24 sex=sex, 25 year_of_birth=year_of_birth, 26 ) 27 my_expectation = dict( 28 first_name=first_name, 29 last_name=last_name, 30 sex=sex, 31 # age=2026-year_of_birth, 32 # age=( 33 # datetime.datetime.today().year 34 # - year_of_birth 35 # ), 36 age=this_year-year_of_birth, 37 ) 38 self.assertEqual(reality, my_expectation) 39 40 41# Exceptions seenI add a git commit message in the other terminal
git commit -am 'use random values for age'the terminal shows a summary of the changes then goes back to the command line.
test factory with random sex
RED: make it fail
I go back to the terminal that is running the tests
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 # year_of_birth = 1996 15 this_year = datetime.datetime.now().year 16 year_of_birth = random.randint( 17 # datetime.datetime.now().year-120, 18 # datetime.datetime.now().year 19 this_year-120, this_year 20 )I use ctrl+s (Windows/Linux) or command+s (MacOS) to run the test a few times and it passes if
sexis randomly'F'.If
sexis randomly'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.choice()is a call to the random.choice method from the random module, it returns a random value from the iterable it is given in parentheses('F', 'M')is a tuple (an iterable) with values for the random.choice method to pick from randomlyrandom.choice(('F', 'M'))randomly gives meForMevery time the test runs
GREEN: make it pass
I add the
sexinput parameter instead of a value that does not change, to the return statement inperson.py4def 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+s (Windows/Linux) or command+s (MacOS) to run the test a few times and it passes with no more random failures
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
RED: make it fail
I go back to the terminal that is running the tests
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 = 'F' 16 sex = random.choice(('F', 'M')) 17 # year_of_birth = 1996 18 this_year = datetime.datetime.now().year 19 year_of_birth = random.randint( 20 # datetime.datetime.now().year-120, 21 # datetime.datetime.now().year 22 this_year-120, this_year 23 )I use ctrl+s (Windows/Linux) or command+s (MacOS) to run the test a few times and it passes if
last_nameis'doe'.If
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,Xis the random age, andYis the random sex.
GREEN: make it pass
I use the
last_nameinput parameter as the value for the'last_name'key in the return statement inperson.py4def 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+s (Windows/Linux) or command+s (MacOS) to run the test a few times and it passes with no more random failures
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
RED: make it fail
I go back to the terminal that is running the tests
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 = 'doe' 15 last_name = random.choice(( 16 'doe', 'smith', 'blow', 'public', 17 )) 18 # sex = 'F' 19 sex = random.choice(('F', 'M')) 20 # year_of_birth = 1996 21 this_year = datetime.datetime.now().year 22 year_of_birth = random.randint( 23 # datetime.datetime.now().year-120, 24 # datetime.datetime.now().year 25 this_year-120, this_year 26 )I use ctrl+s (Windows/Linux) or command+s (MacOS) to run the test a few times and it passes if
first_nameis'jane'.If
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,Xis the random age,Yis the random sex, andZis the random last name
GREEN: make it pass
I add the first_name input parameter instead of a value that does not change, to 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 '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+s (Windows/Linux) or command+s (MacOS) to run the test a few times and it passes with no more random failures
REFACTOR: make it better
I remove the commented lines
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 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
I go back to the terminal that is running the tests
I add a function for the calls to the random.choice method
1import datetime 2import random 3import src.person 4import unittest 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 to remove repetition of
random.choicefrom the test13 def test_factory_w_keyword_arguments(self): 14 # first_name = 'jane' 15 # first_name = random.choice(( 16 # 'jane', 'joe', 'john', 'person', 17 # )) 18 first_name = pick_one( 19 'jane', 'joe', 'john', 'person', 20 ) 21 # last_name = 'doe' 22 # last_name = random.choice(( 23 # 'doe', 'smith', 'blow', 'public', 24 # )) 25 last_name = pick_one( 26 'doe', 'smith', 'blow', 'public', 27 ) 28 # sex = 'F' 29 # sex = random.choice(('F', 'M')) 30 sex = pick_one('F', 'M') 31 # year_of_birth = 1996 32 this_year = datetime.datetime.now().year 33 year_of_birth = random.randint( 34 # datetime.datetime.now().year-120, 35 # datetime.datetime.now().year 36 this_year-120, this_year 37 )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 for
first_name, four forlast_nameand two forsex.I want the function to be able to take any number of arguments I send, without it knowing how many I will send.
I change the definition of the
pick_onefunction with a starred expression like I did in test_w_unknown_arguments so that it can take any number of positional arguments7def pick_one(*choices): 8 return random.choice(choices)the test is green again, because Python reads the positional arguments as a tuple in the function since I used a starred expression (
*choices)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 and the expected dictionary in the test is that one has a year of birth and the other does a calculation with the year of birth. The other things are the same.
RED: make it fail
I go back to the terminal that is running the tests
I add a dictionary to use to remove the repeating parts
13 def test_factory_w_keyword_arguments(self): 14 # first_name = 'jane' 15 # first_name = random.choice(( 16 # 'jane', 'joe', 'john', 'person', 17 # )) 18 first_name = pick_one( 19 'jane', 'joe', 'john', 'person', 20 ) 21 # last_name = 'doe' 22 # last_name = random.choice(( 23 # 'doe', 'smith', 'blow', 'public', 24 # )) 25 last_name = pick_one( 26 'doe', 'smith', 'blow', 'public', 27 ) 28 # sex = 'F' 29 # sex = random.choice(('F', 'M')) 30 sex = pick_one('F', 'M') 31 # year_of_birth = 1996 32 this_year = datetime.datetime.now().year 33 year_of_birth = random.randint( 34 # datetime.datetime.now().year-120, 35 # datetime.datetime.now().year 36 this_year-120, this_year 37 ) 38 39 a_person = dict( 40 first_name=first_name, 41 last_name=last_name, 42 sex=sex, 43 ) 44 45 reality = src.person.factory( 46 first_name=first_name, 47 last_name=last_name, 48 sex=sex, 49 year_of_birth=year_of_birth, 50 ) 51 my_expectation = dict( 52 first_name=first_name, 53 last_name=last_name, 54 sex=sex, 55 # age=2026-year_of_birth, 56 # age=( 57 # datetime.datetime.today().year 58 # - year_of_birth 59 # ), 60 age=this_year-year_of_birth, 61 ) 62 self.assertEqual(reality, my_expectation) 63 64 65# Exceptions seenI use the new variable to remove the repeating parts
39 a_person = dict( 40 first_name=first_name, 41 last_name=last_name, 42 sex=sex, 43 ) 44 45 reality = src.person.factory( 46 # first_name=first_name, 47 # last_name=last_name, 48 # sex=sex, 49 a_person, 50 year_of_birth=year_of_birth, 51 ) 52 my_expectation = dict( 53 # first_name=first_name, 54 # last_name=last_name, 55 # sex=sex, 56 a_person, 57 # age=2026-year_of_birth, 58 # age=( 59 # datetime.datetime.today().year 60 # - year_of_birth 61 # ), 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 TypeError
TypeError: factory() missing 2 required positional arguments: 'last_name' and 'sex'because the factory function now takes
a_personas the first positional argument (first_name), and wants the other required arguments.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 expression for the dictionary like I did in test_w_unknown_arguments
45 reality = src.person.factory(
46 # first_name=first_name,
47 # last_name=last_name,
48 # sex=sex,
49 **a_person,
50 year_of_birth=year_of_birth,
51 )
52 my_expectation = dict(
53 # first_name=first_name,
54 # last_name=last_name,
55 # sex=sex,
56 **a_person,
57 # age=2026-year_of_birth,
58 # age=(
59 # datetime.datetime.today().year
60 # - year_of_birth
61 # ),
62 age=this_year-year_of_birth,
63 )
64 self.assertEqual(reality, my_expectation)
65
66
67# Exceptions seen
the test is green again, because Python sends the dictionary as keyword arguments since I used a double starred expression (*choices).
REFACTOR: make it better
I use the values of
first_name,last_nameand thesexvariables in thea_persondictionary because they are now only used once, bya_person13 def test_factory_w_keyword_arguments(self): 14 # first_name = 'jane' 15 # first_name = random.choice(( 16 # 'jane', 'joe', 'john', 'person', 17 # )) 18 # first_name = pick_one( 19 # 'jane', 'joe', 'john', 'person', 20 # ) 21 # last_name = 'doe' 22 # last_name = random.choice(( 23 # 'doe', 'smith', 'blow', 'public', 24 # )) 25 # last_name = pick_one( 26 # 'doe', 'smith', 'blow', 'public', 27 # ) 28 # sex = 'F' 29 # sex = random.choice(('F', 'M')) 30 # sex = pick_one('F', 'M') 31 # year_of_birth = 1996 32 this_year = datetime.datetime.now().year 33 year_of_birth = random.randint( 34 # datetime.datetime.now().year-120, 35 # datetime.datetime.now().year 36 this_year-120, this_year 37 ) 38 39 a_person = dict( 40 # first_name=first_name, 41 # last_name=last_name, 42 # sex=sex, 43 first_name=pick_one( 44 'jane', 'joe', 'john', 'person', 45 ), 46 last_name=pick_one( 47 'doe', 'smith', 'blow', 'public', 48 ), 49 sex=pick_one('F', 'M'), 50 )still green.
I make 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_persondictionary46 a_person = dict( 47 # first_name=first_name, 48 # last_name=last_name, 49 # sex=sex, 50 # first_name=pick_one( 51 # 'jane', 'joe', 'john', 'person', 52 # ), 53 # last_name=pick_one( 54 # 'doe', 'smith', 'blow', 'public', 55 # ), 56 first_name=get_random_name(), 57 last_name=get_random_name(), 58 sex=pick_one('F', 'M'), 59 )green.
I remove the commented lines
20 def test_factory_w_keyword_arguments(self): 21 this_year = datetime.datetime.now().year 22 year_of_birth = random.randint( 23 this_year-120, this_year 24 ) 25 26 a_person = dict( 27 first_name=get_random_name(), 28 last_name=get_random_name(), 29 sex=pick_one('F', 'M'), 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 that is running the tests
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 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 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 make it a choice.
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=None, 6 sex, year_of_birth, 7 ):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=None, 6 sex=None, year_of_birth, 7 ):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=None, 6 sex=None, year_of_birth=None, 7 ):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}where
Xis the random age,Yis the random sex andZis the random first namethe factory function returns a dictionary with a
'last_name'key, and the assertion expects a dictionary without that key
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='doe', 6 sex=None, year_of_birth=None, 7 ):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': Z, 'last_name': Y, 'sex': None, 'age': X} != {'first_name': Z, 'last_name': Y, 'age': X}the factory function returns a dictionary with a
'sex'key, and the assertion expects a dictionary without that keyI 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='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': datetime.datetime.now().year-year_of_birth, 13 }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 this_year = datetime.datetime.now().year 45 year_of_birth = random.randint( 46 this_year-120, this_year 47 )I use the variable for
realityandmy_expectation55 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 terminal is my friend, and shows TypeError
TypeError: src.person.factory() got multiple values for keyword argument 'first_name'because the
a_persondictionary has a key calledfirst_name, the call tosrc.person.factorygets called with the same keyword argument twice.I comment out
**a_person,Tip
I can comment out a line in Visual Studio Code with ctrl/command+/ anywhere on the line
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 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 seenI use ctrl+s (Windows/Linux) or command+s (MacOS) to run the test a few times and it passes with no more random failures
I remove the commented lines and the
a_persondictionary42 def test_factory_w_optional_arguments(self): 43 first_name = get_random_name() 44 this_year = datetime.datetime.now().year 45 year_of_birth = random.randint( 46 this_year-120, this_year 47 ) 48 49 reality = src.person.factory( 50 first_name=first_name, 51 year_of_birth=year_of_birth, 52 ) 53 my_expectation = dict( 54 first_name=first_name, 55 last_name='doe', 56 sex='M', 57 age=this_year-year_of_birth, 58 ) 59 self.assertEqual(reality, my_expectation) 60 61 62# Exceptions seenI 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.py49 reality = src.person.factory( 50 first_name=first_name, 51 year_of_birth=year_of_birth, 52 ) 53 my_expectation = dict( 54 first_name=first_name, 55 last_name='doe', 56 sex='M', 57 age=this_year-year_of_birth, 58 ) 59 self.assertEqual(reality, my_expectation) 60 61 def test_factory_person_says_hello(self): 62 joe = src.person.factory( 63 first_name='joe', 64 last_name='blow', 65 year_of_birth=1996, 66 ) 67 68 reality = src.person.say_hello(joe) 69 my_expectation = None 70 self.assertEqual(reality, my_expectation) 71 72 73# Exceptions seenthe terminal is my friend, and shows AttributeError
AttributeError: module 'src.person' has no attribute '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: 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
19def say_hello(person): 20 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.py19def say_hello(person): 20 # return None 21 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.py68 reality = src.person.say_hello(joe) 69 # my_expectation = None 70 my_expectation = ( 71 'Hi, my name is joe blow and I am' 72 f' {datetime.datetime.now().year-1996}' 73 ) 74 self.assertEqual(reality, my_expectation) 75 76 jane = src.person.factory( 77 first_name='jane', 78 sex='F', 79 year_of_birth=1991, 80 ) 81 82 reality = src.person.say_hello(jane) 83 my_expectation = ( 84 'Hi, my name is jane doe and I am' 85 f' {datetime.datetime.now().year-1991}' 86 ) 87 self.assertEqual(reality, my_expectation) 88 89 90# 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.py19def say_hello(person): 20 first_name = person.get('first_name') 21 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 first names are the same because
person.get('first_name')uses the get method of the dictionary to get the value of the'first_name'keythe last names and ages are different
I use the get method of the dictionary to get the value for the
last_namekey, then add it to the return statement19def say_hello(person): 20 first_name = person.get('first_name') 21 last_name = person.get('last_name') 22 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 age is the only thing that is different
I use the get method of the dictionary to get the value for the
agekey , then add it to the return statement19def 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 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.
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.py76 jane = src.person.factory( 77 first_name='jane', 78 sex='F', 79 year_of_birth=1991, 80 ) 81 82 reality = src.person.say_hello(jane) 83 my_expectation = ( 84 'Hi, my name is jane doe and I am' 85 f' {datetime.datetime.now().year-1991}' 86 ) 87 self.assertEqual(reality, my_expectation) 88 89 john = src.person.factory( 90 first_name='john', 91 last_name='smith', 92 year_of_birth=1580, 93 ) 94 95 reality = src.person.say_hello(john) 96 my_expectation = ( 97 'Hi, my name is jane doe and I am' 98 f' {datetime.datetime.now().year-1991}' 99 ) 100 self.assertEqual(reality, my_expectation) 101 102 103# 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 matchrealityforjohn89 john = src.person.factory( 90 first_name='john', 91 last_name='smith', 92 year_of_birth=1580, 93 ) 94 95 reality = src.person.say_hello(john) 96 my_expectation = ( 97 # 'Hi, my name is jane doe and I am' 98 # f' {datetime.datetime.now().year-1991}' 99 'Hi, my name is john smith and I am' 100 f' {datetime.datetime.now().year-1580}' 101 ) 102 self.assertEqual(reality, my_expectation) 103 104 105# Exceptions seenthe test passes.
I add an assertion for one more person
95 reality = src.person.say_hello(john) 96 my_expectation = ( 97 # 'Hi, my name is jane doe and I am' 98 # f' {datetime.datetime.now().year-1991}' 99 'Hi, my name is john smith and I am' 100 f' {datetime.datetime.now().year-1580}' 101 ) 102 self.assertEqual(reality, my_expectation) 103 104 a_person = src.person.factory( 105 first_name='person', 106 last_name='public', 107 year_of_birth=2000, 108 sex='F', 109 ) 110 111 reality = src.person.say_hello(a_person) 112 my_expectation = ( 113 'Hi, my name is john smith and I am' 114 f' {datetime.datetime.now().year-1580}' 115 ) 116 self.assertEqual(reality, my_expectation) 117 118 119# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: 'Hi, my name is person public and I am 26' != 'Hi, my name is john smith and I am 446'I change
my_expectationto matchrealityfora_person104 a_person = src.person.factory( 105 first_name='person', 106 last_name='public', 107 year_of_birth=2000, 108 sex='F', 109 ) 110 111 reality = src.person.say_hello(a_person) 112 my_expectation = ( 113 # 'Hi, my name is john smith and I am' 114 # f' {datetime.datetime.now().year-1580}' 115 'Hi, my name is person public and I am' 116 f' {datetime.datetime.now().year-2000}' 117 ) 118 self.assertEqual(reality, my_expectation) 119 120 121# Exceptions seenthe test passes.
I add variables to use them to remove repetition of
'person','public',2000and the age calculation from the last assertion95 reality = src.person.say_hello(john) 96 my_expectation = ( 97 # 'Hi, my name is jane doe and I am' 98 # f' {datetime.datetime.now().year-1991}' 99 'Hi, my name is john smith and I am' 100 f' {datetime.datetime.now().year-1580}' 101 ) 102 self.assertEqual(reality, my_expectation) 103 104 first_name = 'person' 105 last_name = 'public' 106 year_of_birth = 2000 107 age = ( 108 datetime.datetime.now().year 109 - year_of_birth 110 ) 111 112 a_person = src.person.factory( 113 first_name='person', 114 last_name='public', 115 year_of_birth=2000, 116 sex='F', 117 )I use the variables to remove repetition of
'person','public',2000and the age calculation104 first_name = 'person' 105 last_name = 'public' 106 year_of_birth = 2000 107 age = ( 108 datetime.datetime.now().year 109 - year_of_birth 110 ) 111 112 a_person = src.person.factory( 113 # first_name='person', 114 # last_name='public', 115 # year_of_birth=2000, 116 first_name=first_name, 117 last_name=last_name, 118 year_of_birth=year_of_birth, 119 sex='F', 120 ) 121 122 reality = src.person.say_hello(a_person) 123 my_expectation = ( 124 # 'Hi, my name is john smith and I am' 125 # f' {datetime.datetime.now().year-1580}' 126 # 'Hi, my name is person public and I am' 127 # f' {datetime.datetime.now().year-2000}' 128 f'Hi, my name is {first_name} {last_name}' 129 f' and I am {age}' 130 ) 131 self.assertEqual(reality, my_expectation) 132 133 134# Exceptions seenthe test is still green.
I add variables to use them to remove repetition of
'john','smith',1580and the age calculation from the assertion before the one fora_person82 reality = src.person.say_hello(jane) 83 my_expectation = ( 84 'Hi, my name is jane doe and I am' 85 f' {datetime.datetime.now().year-1991}' 86 ) 87 self.assertEqual(reality, my_expectation) 88 89 first_name = 'john' 90 last_name = 'smith' 91 year_of_birth = 1580 92 age = ( 93 datetime.datetime.now().year 94 - year_of_birth 95 ) 96 97 john = src.person.factory( 98 first_name='john', 99 last_name='smith', 100 year_of_birth=1580, 101 )I use the variables to remove repetition of
'john','smith',1580and the age calculation68 reality = src.person.say_hello(joe) 69 # my_expectation = None 70 my_expectation = ( 71 'Hi, my name is joe blow and I am' 72 f' {datetime.datetime.now().year-1996}' 73 ) 74 self.assertEqual(reality, my_expectation) 75 76 first_name = 'jane' 77 last_name = 'doe' 78 year_of_birth = 1991 79 age = ( 80 datetime.datetime.now().year 81 - year_of_birth 82 ) 83 84 jane = src.person.factory( 85 first_name='jane', 86 sex='F', 87 year_of_birth=1991, 88 )still green.
I add the same variable names to use them to remove repetition of
'jane',1991and the age calculation from the second assertion68 reality = src.person.say_hello(joe) 69 # my_expectation = None 70 my_expectation = ( 71 'Hi, my name is joe blow and I am' 72 f' {datetime.datetime.now().year-1996}' 73 ) 74 self.assertEqual(reality, my_expectation) 75 76 first_name = 'jane' 77 last_name = 'doe' 78 year_of_birth = 1991 79 age = ( 80 datetime.datetime.now().year 81 - year_of_birth 82 ) 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 calculation76 first_name = 'jane' 77 last_name = 'doe' 78 year_of_birth = 1991 79 age = ( 80 datetime.datetime.now().year 81 - year_of_birth 82 ) 83 84 jane = src.person.factory( 85 # first_name='jane', 86 sex='F', 87 # year_of_birth=1991, 88 first_name=first_name, 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'Hi, my name is {first_name} {last_name}' 97 f' and I am {age}' 98 ) 99 self.assertEqual(reality, my_expectation) 100 101 first_name = 'john' 102 last_name = 'smith' 103 year_of_birth = 1580 104 age = ( 105 datetime.datetime.now().year 106 - year_of_birth 107 )green.
I add the variable names to use them to remove repetition of
'joe','blow',1996and the age calculation from the first assertion61 def test_factory_person_says_hello(self): 62 first_name = 'joe' 63 last_name = 'blow' 64 year_of_birth = 1996 65 age = ( 66 datetime.datetime.now().year 67 - year_of_birth 68 ) 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 calculation61 def test_factory_person_says_hello(self): 62 first_name = 'joe' 63 last_name = 'blow' 64 year_of_birth = 1996 65 age = ( 66 datetime.datetime.now().year 67 - year_of_birth 68 ) 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'Hi, my name is {first_name} {last_name}' 85 f' and I am {age}' 86 ) 87 self.assertEqual(reality, my_expectation) 88 89 first_name = 'jane' 90 last_name = 'doe' 91 year_of_birth = 1991 92 age = ( 93 datetime.datetime.now().year 94 - year_of_birth 95 )the test is still green.
I remove the commented lines
61 def test_factory_person_says_hello(self): 62 first_name = 'joe' 63 last_name = 'blow' 64 year_of_birth = 1996 65 age = ( 66 datetime.datetime.now().year 67 - year_of_birth 68 ) 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} {last_name}' 79 f' and I am {age}' 80 ) 81 self.assertEqual(reality, my_expectation) 82 83 first_name = 'jane' 84 last_name = 'doe' 85 year_of_birth = 1991 86 age = ( 87 datetime.datetime.now().year 88 - year_of_birth 89 ) 90 91 jane = src.person.factory( 92 sex='F', 93 first_name=first_name, 94 year_of_birth=year_of_birth, 95 ) 96 97 reality = src.person.say_hello(jane) 98 my_expectation = ( 99 f'Hi, my name is {first_name} {last_name}' 100 f' and I am {age}' 101 ) 102 self.assertEqual(reality, my_expectation) 103 104 first_name = 'john' 105 last_name = 'smith' 106 year_of_birth = 1580 107 age = ( 108 datetime.datetime.now().year 109 - year_of_birth 110 ) 111 112 john = src.person.factory( 113 first_name=first_name, 114 last_name=last_name, 115 year_of_birth=year_of_birth, 116 ) 117 118 reality = src.person.say_hello(john) 119 my_expectation = ( 120 f'Hi, my name is {first_name} {last_name}' 121 f' and I am {age}' 122 ) 123 self.assertEqual(reality, my_expectation) 124 125 first_name = 'person' 126 last_name = 'public' 127 year_of_birth = 2000 128 age = ( 129 datetime.datetime.now().year 130 - year_of_birth 131 ) 132 133 a_person = src.person.factory( 134 first_name=first_name, 135 last_name=last_name, 136 year_of_birth=year_of_birth, 137 sex='F', 138 ) 139 140 reality = src.person.say_hello(a_person) 141 my_expectation = ( 142 f'Hi, my name is {first_name} {last_name}' 143 f' and I am {age}' 144 ) 145 self.assertEqual(reality, my_expectation) 146 147 148# 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 calculate_age function
Each assertion in every test has a calculation for the age
I go back to the terminal that is running the tests
I add a function
11def get_random_name(): 12 return pick_one( 13 'jane', 'joe', 'john', 'person', 14 'doe', 'smith', 'blow', 'public', 15 ) 16 17 18def calculate_age(year_of_birth): 19 return ( 20 datetime.datetime.now().year 21 - year_of_birth 22 ) 23 24 25class TestPerson(unittest.TestCase): 26 27 def test_factory_w_keyword_arguments(self):I use the function to remove repetition of the age calculation from
my_expectationin test_factory_w_keyword_arguments39 reality = src.person.factory( 40 **a_person, 41 year_of_birth=year_of_birth, 42 ) 43 my_expectation = dict( 44 **a_person, 45 # age=this_year-year_of_birth, 46 age=calculate_age(year_of_birth), 47 ) 48 self.assertEqual(reality, my_expectation) 49 50 def test_factory_w_optional_arguments(self):the test is still green.
I remove the commented line
27 def test_factory_w_keyword_arguments(self): 28 this_year = datetime.datetime.now().year 29 year_of_birth = random.randint( 30 this_year-120, this_year 31 ) 32 33 a_person = dict( 34 first_name=get_random_name(), 35 last_name=get_random_name(), 36 sex=pick_one('F', 'M'), 37 ) 38 39 reality = src.person.factory( 40 **a_person, 41 year_of_birth=year_of_birth, 42 ) 43 my_expectation = dict( 44 **a_person, 45 age=calculate_age(year_of_birth), 46 ) 47 self.assertEqual(reality, my_expectation) 48 49 def test_factory_w_optional_arguments(self):I use the function to remove repetition of the age calculation from
my_expectationin test_factory_w_optional_arguments56 reality = src.person.factory( 57 first_name=first_name, 58 year_of_birth=year_of_birth, 59 ) 60 my_expectation = dict( 61 first_name=first_name, 62 last_name='doe', 63 sex='M', 64 # age=this_year-year_of_birth, 65 age=calculate_age(year_of_birth), 66 ) 67 self.assertEqual(reality, my_expectation) 68 69 def test_factory_person_says_hello(self):the test is still green.
I remove the commented line
49 def test_factory_w_optional_arguments(self): 50 first_name = get_random_name() 51 this_year = datetime.datetime.now().year 52 year_of_birth = random.randint( 53 this_year-120, this_year 54 ) 55 56 reality = src.person.factory( 57 first_name=first_name, 58 year_of_birth=year_of_birth, 59 ) 60 my_expectation = dict( 61 first_name=first_name, 62 last_name='doe', 63 sex='M', 64 age=calculate_age(year_of_birth), 65 ) 66 self.assertEqual(reality, my_expectation) 67 68 def test_factory_person_says_hello(self):I use the function to remove repetition of the age calculation from
my_expectationforjoein test_factory_person_says_hello68 def test_factory_person_says_hello(self): 69 first_name = 'joe' 70 last_name = 'blow' 71 year_of_birth = 1996 72 # age = ( 73 # datetime.datetime.now().year 74 # - year_of_birth 75 # ) 76 77 joe = src.person.factory( 78 first_name=first_name, 79 last_name=last_name, 80 year_of_birth=year_of_birth, 81 ) 82 83 reality = src.person.say_hello(joe) 84 my_expectation = ( 85 f'Hi, my name is {first_name} {last_name}' 86 # f' and I am {age}' 87 f' and I am {calculate_age(year_of_birth)}' 88 ) 89 self.assertEqual(reality, my_expectation)the test is still green.
I use the function to remove repetition of the age calculation from
my_expectationforjanein test_factory_person_says_hello83 reality = src.person.say_hello(joe) 84 my_expectation = ( 85 f'Hi, my name is {first_name} {last_name}' 86 # f' and I am {age}' 87 f' and I am {calculate_age(year_of_birth)}' 88 ) 89 self.assertEqual(reality, my_expectation) 90 91 first_name = 'jane' 92 last_name = 'doe' 93 year_of_birth = 1991 94 # age = ( 95 # datetime.datetime.now().year 96 # - year_of_birth 97 # ) 98 99 jane = src.person.factory( 100 sex='F', 101 first_name=first_name, 102 year_of_birth=year_of_birth, 103 ) 104 105 reality = src.person.say_hello(jane) 106 my_expectation = ( 107 f'Hi, my name is {first_name} {last_name}' 108 # f' and I am {age}' 109 f' and I am {calculate_age(year_of_birth)}' 110 ) 111 self.assertEqual(reality, my_expectation)still green.
I use the function to remove repetition of the age calculation from
my_expectationforjohnin test_factory_person_says_hello105 reality = src.person.say_hello(jane) 106 my_expectation = ( 107 f'Hi, my name is {first_name} {last_name}' 108 # f' and I am {age}' 109 f' and I am {calculate_age(year_of_birth)}' 110 ) 111 self.assertEqual(reality, my_expectation) 112 113 first_name = 'john' 114 last_name = 'smith' 115 year_of_birth = 1580 116 # age = ( 117 # datetime.datetime.now().year 118 # - year_of_birth 119 # ) 120 121 john = src.person.factory( 122 first_name=first_name, 123 last_name=last_name, 124 year_of_birth=year_of_birth, 125 ) 126 127 reality = src.person.say_hello(john) 128 my_expectation = ( 129 f'Hi, my name is {first_name} {last_name}' 130 # f' and I am {age}' 131 f' and I am {calculate_age(year_of_birth)}' 132 ) 133 self.assertEqual(reality, my_expectation)green.
I use the function to remove repetition of the age calculation from
my_expectationfora_personin test_factory_person_says_hello127 reality = src.person.say_hello(john) 128 my_expectation = ( 129 f'Hi, my name is {first_name} {last_name}' 130 # f' and I am {age}' 131 f' and I am {calculate_age(year_of_birth)}' 132 ) 133 self.assertEqual(reality, my_expectation) 134 135 first_name = 'person' 136 last_name = 'public' 137 year_of_birth = 2000 138 # age = ( 139 # datetime.datetime.now().year 140 # - year_of_birth 141 # ) 142 143 a_person = src.person.factory( 144 first_name=first_name, 145 last_name=last_name, 146 year_of_birth=year_of_birth, 147 sex='F', 148 ) 149 150 reality = src.person.say_hello(a_person) 151 my_expectation = ( 152 f'Hi, my name is {first_name} {last_name}' 153 # f' and I am {age}' 154 f' and I am {calculate_age(year_of_birth)}' 155 ) 156 self.assertEqual(reality, my_expectation) 157 158 159# Exceptions seenthe test is still green.
I remove the commented lines
68 def test_factory_person_says_hello(self): 69 first_name = 'joe' 70 last_name = 'blow' 71 year_of_birth = 1996 72 73 joe = src.person.factory( 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 = ( 81 f'Hi, my name is {first_name} {last_name}' 82 f' and I am {calculate_age(year_of_birth)}' 83 ) 84 self.assertEqual(reality, my_expectation) 85 86 first_name = 'jane' 87 last_name = 'doe' 88 year_of_birth = 1991 89 90 jane = src.person.factory( 91 sex='F', 92 first_name=first_name, 93 year_of_birth=year_of_birth, 94 ) 95 96 reality = src.person.say_hello(jane) 97 my_expectation = ( 98 f'Hi, my name is {first_name} {last_name}' 99 f' and I am {calculate_age(year_of_birth)}' 100 ) 101 self.assertEqual(reality, my_expectation) 102 103 first_name = 'john' 104 last_name = 'smith' 105 year_of_birth = 1580 106 107 john = src.person.factory( 108 first_name=first_name, 109 last_name=last_name, 110 year_of_birth=year_of_birth, 111 ) 112 113 reality = src.person.say_hello(john) 114 my_expectation = ( 115 f'Hi, my name is {first_name} {last_name}' 116 f' and I am {calculate_age(year_of_birth)}' 117 ) 118 self.assertEqual(reality, my_expectation) 119 120 first_name = 'person' 121 last_name = 'public' 122 year_of_birth = 2000 123 124 a_person = src.person.factory( 125 first_name=first_name, 126 last_name=last_name, 127 year_of_birth=year_of_birth, 128 sex='F', 129 ) 130 131 reality = src.person.say_hello(a_person) 132 my_expectation = ( 133 f'Hi, my name is {first_name} {last_name}' 134 f' and I am {calculate_age(year_of_birth)}' 135 ) 136 self.assertEqual(reality, my_expectation) 137 138 139# Exceptions seenthe 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 values
I want to use random values to test_factory_person_says_hello
I go back to the terminal that is running the tests
I add random values to use for the
first_name,last_name,sex,year_of_birthandagevariables68 def test_factory_person_says_hello(self): 69 first_name = get_random_name() 70 last_name = get_random_name() 71 sex = pick_one('F', 'M') 72 73 this_year = datetime.datetime.now().year 74 year_of_birth = random.randint( 75 this_year-120, this_year 76 ) 77 age = calculate_age(year_of_birth) 78 79 first_name = 'joe' 80 last_name = 'blow' 81 year_of_birth = 1996I comment out the
first_name,last_nameandyear_of_birthvariables forjoein the first assertion, so it uses the ones with random values68 def test_factory_person_says_hello(self): 69 first_name = get_random_name() 70 last_name = get_random_name() 71 sex = pick_one('F', 'M') 72 73 this_year = datetime.datetime.now().year 74 year_of_birth = random.randint( 75 this_year-120, this_year 76 ) 77 age = calculate_age(year_of_birth) 78 79 # first_name = 'joe' 80 # last_name = 'blow' 81 # year_of_birth = 1996 82 83 joe = src.person.factory( 84 first_name=first_name, 85 last_name=last_name, 86 year_of_birth=year_of_birth, 87 )the test is still green.
I add a random value for the
sexparameter with thesexvariable in the call tosrc.person.factoryforjoe83 joe = src.person.factory( 84 first_name=first_name, 85 last_name=last_name, 86 year_of_birth=year_of_birth, 87 sex=sex, 88 ) 89 90 reality = src.person.say_hello(joe) 91 my_expectation = ( 92 f'Hi, my name is {first_name} {last_name}' 93 f' and I am {calculate_age(year_of_birth)}' 94 ) 95 self.assertEqual(reality, my_expectation)still green.
I use the
agevariable inmy_expectationforjoe90 reality = src.person.say_hello(joe) 91 my_expectation = ( 92 f'Hi, my name is {first_name} {last_name}' 93 # f' and I am {calculate_age(year_of_birth)}' 94 f' and I am {age}' 95 ) 96 self.assertEqual(reality, my_expectation)green.
I comment out the
first_name,last_nameandyear_of_birthvariables forjanein the second assertion, so it uses the ones with random values89 reality = src.person.say_hello(joe) 90 my_expectation = ( 91 f'Hi, my name is {first_name} {last_name}' 92 # f' and I am {calculate_age(year_of_birth)}' 93 f' and I am {age}' 94 ) 95 self.assertEqual(reality, my_expectation) 96 97 # first_name = 'jane' 98 # last_name = 'doe' 99 # year_of_birth = 1991 100 101 jane = src.person.factory( 102 sex='F', 103 first_name=first_name, 104 year_of_birth=year_of_birth, 105 )I use ctrl+s (Windows/Linux) or command+s (MacOS) to run the test a few times and it passes if the
last_nameis randomly'doe'.If the
last_nameis not'doe', the terminal is my friend, and shows AssertionErrorAssertionError: 'Hi, my name is X doe and I am Z' != 'Hi, my name is X Y and I am Z'because I did not need to give a value for the
last_nameparameter in the call tosrc.person.factorysince the default value for thelast_nameparameter of the function is'doe'. This means thatsrc.person.factory( sex='F', first_name=first_name, year_of_birth=year_of_birth, )is the same as
src.person.factory( sex='F', first_name=first_name, 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.
I add
last_nameto the call tosrc.person.factoryforjaneto use the variable102 jane = src.person.factory( 103 sex='F', 104 first_name=first_name, 105 last_name=last_name, 106 year_of_birth=year_of_birth, 107 )I use ctrl+s (Windows/Linux) or command+s (MacOS) to run the test a few times and it passes with no more random failures.
I change the value for the
sexparameter to thesexvariable so it uses a random value in the call tosrc.person.factoryforjane102 jane = src.person.factory( 103 # sex='F', 104 first_name=first_name, 105 last_name=last_name, 106 year_of_birth=year_of_birth, 107 sex=sex, 108 )the test is still green.
I use the
agevariable inmy_expectationforjane110 reality = src.person.say_hello(jane) 111 my_expectation = ( 112 f'Hi, my name is {first_name} {last_name}' 113 # f' and I am {calculate_age(year_of_birth)}' 114 f' and I am {age}' 115 ) 116 self.assertEqual(reality, my_expectation)still green.
I comment out the
first_name,last_nameandyear_of_birthvariables forjohnin the third assertion, so it uses the ones with random values110 reality = src.person.say_hello(jane) 111 my_expectation = ( 112 f'Hi, my name is {first_name} {last_name}' 113 # f' and I am {calculate_age(year_of_birth)}' 114 f' and I am {age}' 115 ) 116 self.assertEqual(reality, my_expectation) 117 118 # first_name = 'john' 119 # last_name = 'smith' 120 # year_of_birth = 1580 121 122 john = src.person.factory( 123 first_name=first_name, 124 last_name=last_name, 125 year_of_birth=year_of_birth, 126 )green.
I add a random value for the
sexparameter with thesexvariable in the call tosrc.person.factoryforjohn122 john = src.person.factory( 123 first_name=first_name, 124 last_name=last_name, 125 year_of_birth=year_of_birth, 126 sex=sex, 127 )still green.
I use the
agevariable inmy_expectationforjohn129 reality = src.person.say_hello(john) 130 my_expectation = ( 131 f'Hi, my name is {first_name} {last_name}' 132 # f' and I am {calculate_age(year_of_birth)}' 133 f' and I am {age}' 134 ) 135 self.assertEqual(reality, my_expectation)the test is still green.
I comment out the
first_name,last_nameandyear_of_birthvariables fora_personin the last assertion, so it uses the ones with random values129 reality = src.person.say_hello(john) 130 my_expectation = ( 131 f'Hi, my name is {first_name} {last_name}' 132 # f' and I am {calculate_age(year_of_birth)}' 133 f' and I am {age}' 134 ) 135 self.assertEqual(reality, my_expectation) 136 137 # first_name = 'person' 138 # last_name = 'public' 139 # year_of_birth = 2000 140 141 a_person = src.person.factory( 142 first_name=first_name, 143 last_name=last_name, 144 year_of_birth=year_of_birth, 145 sex='F', 146 )still green.
I add a random value for the
sexparameter with thesexvariable in the call tosrc.person.factoryfora_person141 a_person = src.person.factory( 142 first_name=first_name, 143 last_name=last_name, 144 year_of_birth=year_of_birth, 145 # sex='F', 146 sex=sex, 147 )green.
I use the
agevariable inmy_expectationfora_person149 reality = src.person.say_hello(a_person) 150 my_expectation = ( 151 f'Hi, my name is {first_name} {last_name}' 152 # f' and I am {calculate_age(year_of_birth)}' 153 f' and I am {age}' 154 ) 155 self.assertEqual(reality, my_expectation)still green.
I add a random person for the four people I made in test_factory_person_says_hello because they all now all use the same random values. The assertions are also the same for each case because they use the
first_name,last_nameandagevariables that are sent in the call tosrc.person.factory149 reality = src.person.say_hello(a_person) 150 my_expectation = ( 151 f'Hi, my name is {first_name} {last_name}' 152 # f' and I am {calculate_age(year_of_birth)}' 153 f' and I am {age}' 154 ) 155 self.assertEqual(reality, my_expectation) 156 157 a_random_person = src.person.factory( 158 first_name=first_name, 159 last_name=last_name, 160 sex=sex, 161 year_of_birth=year_of_birth, 162 ) 163 164 reality = src.person.say_hello(a_random_person) 165 my_expectation = '' 166 self.assertEqual(reality, my_expectation) 167 168 169# Exceptions seenI use ctrl+s (Windows/Linux) or command+s (MacOS) to run the test a few times, and each time the terminal shows AssertionError
AssertionError: 'Hi, my name is Z Y and I am X' != ''where
Xis a random age,Zis a random last name andYis a random first nameI change
my_expectationto matchrealityfora_random_person157 a_random_person = src.person.factory( 158 first_name=first_name, 159 last_name=last_name, 160 sex=sex, 161 year_of_birth=year_of_birth, 162 ) 163 164 reality = src.person.say_hello(a_random_person) 165 my_expectation = ( 166 f'Hi, my name is {first_name} {last_name}' 167 f' and I am {age}' 168 ) 169 self.assertEqual(reality, my_expectation) 170 171 172# Exceptions seenthe test passes.
I remove the commented lines and
joe,jane,johnandpersonfrom test_factory_person_says_hello because they are all the same asa_random_person68 def test_factory_person_says_hello(self): 69 first_name = get_random_name() 70 last_name = get_random_name() 71 sex = pick_one('F', 'M') 72 73 this_year = datetime.datetime.now().year 74 year_of_birth = random.randint( 75 this_year-120, this_year 76 ) 77 age = calculate_age(year_of_birth) 78 79 a_random_person = src.person.factory( 80 first_name=first_name, 81 last_name=last_name, 82 sex=sex, 83 year_of_birth=year_of_birth, 84 ) 85 86 reality = src.person.say_hello(a_random_person) 87 my_expectation = ( 88 f'Hi, my name is {first_name} {last_name}' 89 f' and I am {age}' 90 ) 91 self.assertEqual(reality, my_expectation) 92 93 94# Exceptions seenI add a git commit message in the other terminal
git commit -am \ 'test_factory_person_says_hello with random values'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 that is running the tests
I add a function to use to replace the repetition of making the values for the
this_yearandyear_of_birthvariables intest_person.py18def calculate_age(year_of_birth): 19 return ( 20 datetime.datetime.now().year 21 - year_of_birth 22 ) 23 24 25def get_random_year_of_birth(): 26 this_year = datetime.datetime.now().year 27 return random.randint( 28 this_year-120, this_year 29 ) 30 31 32class TestPerson(unittest.TestCase): 33 34 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_arguments34 def test_factory_w_keyword_arguments(self): 35 # this_year = datetime.datetime.now().year 36 # year_of_birth = random.randint( 37 # this_year-120, this_year 38 # ) 39 year_of_birth = get_random_year_of_birth()the test is still green.
I remove the commented lines
34 def test_factory_w_keyword_arguments(self): 35 year_of_birth = get_random_year_of_birth() 36 37 a_person = dict( 38 first_name=get_random_name(), 39 last_name=get_random_name(), 40 sex=pick_one('F', 'M'), 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 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_arguments53 def test_factory_w_optional_arguments(self): 54 first_name = get_random_name() 55 # this_year = datetime.datetime.now().year 56 # year_of_birth = random.randint( 57 # this_year-120, this_year 58 # ) 59 year_of_birth = get_random_year_of_birth()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 year_of_birth = get_random_year_of_birth() 56 57 reality = src.person.factory( 58 first_name=first_name, 59 year_of_birth=year_of_birth, 60 ) 61 my_expectation = dict( 62 first_name=first_name, 63 last_name='doe', 64 sex='M', 65 age=calculate_age(year_of_birth), 66 ) 67 self.assertEqual(reality, my_expectation) 68 69 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 in test_factory_person_says_hello69 def test_factory_person_says_hello(self): 70 first_name = get_random_name() 71 last_name = get_random_name() 72 sex = pick_one('F', 'M') 73 74 # this_year = datetime.datetime.now().year 75 # year_of_birth = random.randint( 76 # this_year-120, this_year 77 # ) 78 year_of_birth = get_random_year_of_birth() 79 age = calculate_age(year_of_birth)the test is still green.
I remove the commented lines
69 def test_factory_person_says_hello(self): 70 first_name = get_random_name() 71 last_name = get_random_name() 72 sex = pick_one('F', 'M') 73 74 year_of_birth = get_random_year_of_birth() 75 age = calculate_age(year_of_birth) 76 77 a_random_person = src.person.factory( 78 first_name=first_name, 79 last_name=last_name, 80 sex=sex, 81 year_of_birth=year_of_birth, 82 ) 83 84 reality = src.person.say_hello(a_random_person) 85 my_expectation = ( 86 f'Hi, my name is {first_name} {last_name}' 87 f' and I am {age}' 88 ) 89 self.assertEqual(reality, my_expectation) 90 91 92# Exceptions seen 93# AssertionError 94# NameError 95# AttributeError 96# TypeError 97# 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 that is running the tests
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
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 functionI 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+s (Windows/Linux) or command+s (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+s (Windows/Linux) or command+s (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 functionI 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+s (Windows/Linux) or command+s (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.