separate and equal telephone
The text function in the telephone project was written in test_telephone.py. I want to move it to telephone.py in the src folder so that I can keep the tests and solution separate.
preview
I have these tests by the end of the chapter
1import src.telephone
2
3
4def test_passing_none():
5 reality = src.telephone.text(None)
6 my_expectation = 'I got: None'
7 assert reality == my_expectation
8
9
10def test_passing_booleans():
11 reality = src.telephone.text(False)
12 my_expectation = 'I got: False'
13 assert reality == my_expectation
14
15 reality = src.telephone.text(True)
16 my_expectation = 'I got: True'
17 assert reality == my_expectation
18
19
20def test_passing_an_integer():
21 an_integer = 1234
22
23 reality = src.telephone.text(an_integer)
24 my_expectation = f'I got: {an_integer}'
25 assert reality == my_expectation
26
27
28def test_passing_a_float():
29 a_float = 5.678
30
31 reality = src.telephone.text(a_float)
32 my_expectation = f'I got: {a_float}'
33 assert reality == my_expectation
34
35
36def test_passing_a_string():
37 a_string = 'hello'
38
39 reality = src.telephone.text(a_string)
40 my_expectation = f'I got: {a_string}'
41 assert reality == my_expectation
42
43
44def test_passing_a_tuple():
45 a_tuple = (0, 1, 2, 'n')
46
47 reality = src.telephone.text(a_tuple)
48 my_expectation = f'I got: {a_tuple}'
49 assert reality == my_expectation
50
51
52def test_passing_a_list():
53 a_list = [0, 1, 2, 'n']
54
55 reality = src.telephone.text(a_list)
56 my_expectation = f'I got: {a_list}'
57 assert reality == my_expectation
58
59
60def test_passing_a_set():
61 a_set = {0, 1, 2, 'n'}
62
63 reality = src.telephone.text(a_set)
64 my_expectation = f'I got: {a_set}'
65 assert reality == my_expectation
66
67
68def test_passing_a_dictionary():
69 a_dictionary = {
70 'key0': 'value0',
71 'keyN': [0, 1, 2, 'n'],
72 }
73
74 reality = src.telephone.text(a_dictionary)
75 my_expectation = f'I got: {a_dictionary}'
76 assert reality == my_expectation
77
78
79def test_passing_a_class():
80 reality = src.telephone.text(object)
81 my_expectation = "I got: <class 'object'>"
82 assert reality == my_expectation
83
84 reality = src.telephone.text(bool)
85 my_expectation = "I got: <class 'bool'>"
86 assert reality == my_expectation
87
88 reality = src.telephone.text(int)
89 my_expectation = "I got: <class 'int'>"
90 assert reality == my_expectation
91
92 reality = src.telephone.text(float)
93 my_expectation = "I got: <class 'float'>"
94 assert reality == my_expectation
95
96 reality = src.telephone.text(str)
97 my_expectation = "I got: <class 'str'>"
98 assert reality == my_expectation
99
100 reality = src.telephone.text(tuple)
101 my_expectation = "I got: <class 'tuple'>"
102 assert reality == my_expectation
103
104 reality = src.telephone.text(list)
105 my_expectation = "I got: <class 'list'>"
106 assert reality == my_expectation
107
108 reality = src.telephone.text(set)
109 my_expectation = "I got: <class 'set'>"
110 assert reality == my_expectation
111
112 reality = src.telephone.text(dict)
113 my_expectation = "I got: <class 'dict'>"
114 assert reality == my_expectation
115
116
117# Exceptions seen
118# AssertionError
119# NameError
120# TypeError
121# ModuleNotFoundError
122# AttributeError
open the project
I open a terminal
I change directory to the project
cd telephonethe terminal shows I am in the
telephonefolder.../pumping_python/telephoneI open
test_telephone.pyI use pytest-watcher to run the tests automatically
uv run pytest-watcher . --nowthe terminal shows
test_telephone.py .......... [100%] =================== 10 passed in A.BCs ===================
move test_passing_none
RED: make it fail
I change the call in the assertion of test_passing_none to use the result of a call to the text function of the telephone module in the src folder instead of a call to the text function in test_telephone.py
1def text(the_input):
2 return f'I got: {the_input}'
3
4
5def test_passing_none():
6 reality = src.telephone.text(None)
7 my_expectation = 'I got: None'
8 # assert text(None) == 'I got: None'
9 assert reality == my_expectation
10
11
12def test_passing_booleans():
the terminal is my friend, and shows NameError
NameError: name 'src' is not defined
because there is nothing with that name in test_telephone.py.
GREEN: make it pass
I add an import statement at the top of
test_telephone.py1import src 2 3 4def text(the_input): 5 return f'I got: {the_input}'the terminal is my friend, and shows ModuleNotFoundError
E ModuleNotFoundError: No module named 'src'because there is nothing named
srcin the project.I add ModuleNotFoundError to the list of Exceptions seen
72# Exceptions seen 73# AssertionError 74# NameError 75# TypeError 76# ModuleNotFoundErrorI open another terminal and make sure I am in the
telephonefolderI use mkdir to make a folder named
srcmkdir srcthe terminal goes back to the command line.
I go back to the terminal where the tests are running.
I use ctrl/command+s (Windows & Linux/MacOS) in
test_telephone.pyto run the test again, and the terminal shows AttributeErrorAttributeError: module 'src' has no attribute 'telephone'because there is nothing named
telephonein thesrcfolder.I add AttributeError to the list of Exceptions seen
72# Exceptions seen 73# AssertionError 74# NameError 75# TypeError 76# ModuleNotFoundError 77# AttributeErrorI change the import statement to make it import
telephone.pyfrom thesrcfolder1# import src 2import src.telephone 3 4 5def text(the_input):the terminal is my friend, and shows ModuleNotFoundError
E ModuleNotFoundError: No module named 'src.telephone'because Python cannot find
telephone.pyin thesrcfolder since I have not made it yet.I go to the other terminal
I use touch to make
telephone.pyin thesrcfoldertouch src/telephone.pyNew-Item src/telephone.pythe terminal goes back to the command line.
I add the new file to git for tracking
git add src/telephone.pythe terminal goes back to the command line.
I go back to the terminal where the tests are running and it shows AttributeError
AttributeError: module 'src.telephone' has no attribute 'text'because
telephone.pyin thesrcfolder does not have anything namedtextinside it.I open
telephone/__init__.pyfrom thesrcfolderI add a copy of the
textfunction totelephone.py1def text(the_input): 2 return f'I got: {the_input}'the test passes because
Python brings in an object for the
telephone.pyfile from thesrcfolder so I can use it intest_telephone.pyassrc.telephonewhenimport src.telephoneruns.Python calls the
textfunction from the object it imported for thetelephone.pyfile from thesrcfolder (src.telephone) whensrc.telephone.text(None)runs.
I think of
src.telephone.textlike an addresssrc.telephone.text src └── telephone.py └── def text(the_input): └── return f'I got: {the_input}'
REFACTOR: make it better
I remove the commented lines from
test_telephone.py1import src.telephone 2 3 4def text(the_input): 5 return f'I got: {the_input}' 6 7 8def test_passing_none(): 9 reality = src.telephone.text(None) 10 my_expectation = 'I got: None' 11 assert reality == my_expectation 12 13 14def test_passing_booleans():I change the call in the first assertion of test_passing_booleans to
src.telephone.text14def test_passing_booleans(): 15 reality = src.telephone.text(False) 16 my_expectation = 'I got: False' 17 # assert text(False) == 'I got: False' 18 assert reality == my_expectation 19 assert text(True) == 'I got: True' 20 21 22def test_passing_an_integer():the test is still green because when
src.telephone.textis called, Python follows this pathsrc.telephone.text src └── telephone.py └── def text(the_input): └── return f'I got: {the_input}'using the string representation of the object in the curly braces
{ }text(None) text(the_input) the_input = None return f'I got: {the_input}' return 'I got: None 'I make the same change for the next assertion
14def test_passing_booleans(): 15 # assert text(False) == 'I got: False' 16 reality = src.telephone.text(False) 17 my_expectation = 'I got: False' 18 assert reality == my_expectation 19 20 reality = src.telephone.text(True) 21 my_expectation = 'I got: True' 22 # assert text(True) == 'I got: True' 23 assert reality == my_expectation 24 25 26def test_passing_an_integer():still green.
I remove the commented lines from test_passing_booleans
14def test_passing_booleans(): 15 reality = src.telephone.text(False) 16 my_expectation = 'I got: False' 17 assert reality == my_expectation 18 19 reality = src.telephone.text(True) 20 my_expectation = 'I got: True' 21 assert reality == my_expectation 22 23 24def test_passing_an_integer():I change the call in the assertion of test_passing_an_integer to
src.telephone.text24def test_passing_an_integer(): 25 an_integer = 1234 26 27 reality = src.telephone.text(an_integer) 28 my_expectation = f'I got: {an_integer}' 29 # assert text(an_integer) == f'I got: {an_integer}' 30 assert reality == my_expectation 31 32 33def test_passing_a_float():green.
I remove the commented line from test_passing_an_integer
24def test_passing_an_integer(): 25 an_integer = 1234 26 27 reality = src.telephone.text(an_integer) 28 my_expectation = f'I got: {an_integer}' 29 assert reality == my_expectation 30 31 32def test_passing_a_float():I change the call in the assertion of test_passing_a_float
32def test_passing_a_float(): 33 a_float = 5.678 34 35 reality = src.telephone.text(a_float) 36 my_expectation = f'I got: {a_float}' 37 # assert text(a_float) == f'I got: {a_float}' 38 assert reality == my_expectation 39 40 41def test_passing_a_string():still green because when
src.telephone.textis called, Python follows this pathsrc.telephone.text src └── telephone.py └── def text(the_input): └── return f'I got: {the_input}'using the string representation of the object in the curly braces
{ }a_float = 5.678 text(a_float) text(the_input) the_input = 5.678 return f'I got: {the_input}' return 'I got: 5.678 'I remove the commented line
32def test_passing_a_float(): 33 a_float = 5.678 34 35 reality = src.telephone.text(a_float) 36 my_expectation = f'I got: {a_float}' 37 assert reality == my_expectation 38 39 40def test_passing_a_string():the test is still green.
I change the call in the assertion of test_passing_a_string
40def test_passing_a_string(): 41 a_string = 'hello' 42 43 reality = src.telephone.text(a_string) 44 my_expectation = f'I got: {a_string}' 45 # assert text(a_string) == f'I got: {a_string}' 46 assert reality == my_expectation 47 48 49def test_passing_a_tuple():still green.
I remove the commented line
40def test_passing_a_string(): 41 a_string = 'hello' 42 43 reality = src.telephone.text(a_string) 44 my_expectation = f'I got: {a_string}' 45 assert reality == my_expectation 46 47 48def test_passing_a_tuple():I change the call in the assertion of test_passing_a_tuple
48def test_passing_a_tuple(): 49 a_tuple = (0, 1, 2, 'n') 50 51 reality = src.telephone.text(a_tuple) 52 my_expectation = f'I got: {a_tuple}' 53 # assert text(a_tuple) == f'I got: {a_tuple}' 54 assert reality == my_expectation 55 56 57def test_passing_a_list():green.
I remove the commented line
48def test_passing_a_tuple(): 49 a_tuple = (0, 1, 2, 'n') 50 51 reality = src.telephone.text(a_tuple) 52 my_expectation = f'I got: {a_tuple}' 53 assert reality == my_expectation 54 55 56def test_passing_a_list():I change the call in the assertion of test_passing_a_list
56def test_passing_a_list(): 57 a_list = [0, 1, 2, 'n'] 58 59 reality = src.telephone.text(a_list) 60 my_expectation = f'I got: {a_list}' 61 # assert text(a_list) == f'I got: {a_list}' 62 assert reality == my_expectation 63 64 65def test_passing_a_set():still green.
I remove the commented line
56def test_passing_a_list(): 57 a_list = [0, 1, 2, 'n'] 58 59 reality = src.telephone.text(a_list) 60 my_expectation = f'I got: {a_list}' 61 assert reality == my_expectation 62 63 64def test_passing_a_set():I change the call in the assertion of test_passing_a_set
64def test_passing_a_set(): 65 a_set = {0, 1, 2, 'n'} 66 67 reality = src.telephone.text(a_set) 68 my_expectation = f'I got: {a_set}' 69 # assert text(a_set) == f'I got: {a_set}' 70 assert reality == my_expectation 71 72 73def test_passing_a_dictionary():the test is still green.
I remove the commented line
64def test_passing_a_set(): 65 a_set = {0, 1, 2, 'n'} 66 67 reality = src.telephone.text(a_set) 68 my_expectation = f'I got: {a_set}' 69 assert reality == my_expectation 70 71 72def test_passing_a_dictionary():I change the value the
realityvariable of test_passing_a_dictionary points to from the result of a call to thetextfunction of thetest_telephonemodule, to the result of a call to thetextfunction of thetelephonemodule in thesrcfolder72def test_passing_a_dictionary(): 73 a_dictionary = { 74 'key0': 'value0', 75 'keyN': [0, 1, 2, 'n'], 76 } 77 # reality = text(a_dictionary) 78 reality = src.telephone.text(a_dictionary) 79 my_expectation = f'I got: {a_dictionary}' 80 assert reality == my_expectation 81 82 83def test_passing_a_class():still green because when
src.telephone.textis called, Python follows this pathsrc.telephone.text src └── telephone.py └── def text(the_input): └── return f'I got: {the_input}'using the string representation of the object in the curly braces
{ }a_dictionary = { 'key0': 'value0', 'keyN': [0, 1, 2, 'n'], } text(a_dictionary) text(the_input) the_input = { 'key0': 'value0', 'keyN': [0, 1, 2, 'n'], } return f'I got: {the_input}' return ("I got: {'key0': 'value0'," 'keyN': [0, 1, 2, 'n']}")I remove the commented line
72def test_passing_a_dictionary(): 73 a_dictionary = { 74 'key0': 'value0', 75 'keyN': [0, 1, 2, 'n'], 76 } 77 78 reality = src.telephone.text(a_dictionary) 79 my_expectation = f'I got: {a_dictionary}' 80 assert reality == my_expectation 81 82 83def test_passing_a_class():I change the calls in the assertions of test_passing_a_class
83def test_passing_a_class(): 84 reality = src.telephone.text(object) 85 my_expectation = "I got: <class 'object'>" 86 # assert text(object) == "I got: <class 'object'>" 87 assert reality == my_expectation 88 89 reality = src.telephone.text(bool) 90 my_expectation = "I got: <class 'bool'>" 91 # assert text(bool) == "I got: <class 'bool'>" 92 assert reality == my_expectation 93 94 reality = src.telephone.text(int) 95 my_expectation = "I got: <class 'int'>" 96 # assert text(int) == "I got: <class 'int'>" 97 assert reality == my_expectation 98 99 reality = src.telephone.text(float) 100 my_expectation = "I got: <class 'float'>" 101 # assert text(float) == "I got: <class 'float'>" 102 assert reality == my_expectation 103 104 reality = src.telephone.text(str) 105 my_expectation = "I got: <class 'str'>" 106 # assert text(str) == "I got: <class 'str'>" 107 assert reality == my_expectation 108 109 reality = src.telephone.text(tuple) 110 my_expectation = "I got: <class 'tuple'>" 111 # assert text(tuple) == "I got: <class 'tuple'>" 112 assert reality == my_expectation 113 114 reality = src.telephone.text(list) 115 my_expectation = "I got: <class 'list'>" 116 # assert text(list) == "I got: <class 'list'>" 117 assert reality == my_expectation 118 119 reality = src.telephone.text(set) 120 my_expectation = "I got: <class 'set'>" 121 # assert text(set) == "I got: <class 'set'>" 122 assert reality == my_expectation 123 124 reality = src.telephone.text(dict) 125 my_expectation = "I got: <class 'dict'>" 126 # assert text(dict) == "I got: <class 'dict'>" 127 assert reality == my_expectation 128 129 130# Exceptions seenthe test is still green.
I remove the commented lines from test_passing_a_class
83def test_passing_a_class(): 84 reality = src.telephone.text(object) 85 my_expectation = "I got: <class 'object'>" 86 assert reality == my_expectation 87 88 reality = src.telephone.text(bool) 89 my_expectation = "I got: <class 'bool'>" 90 assert reality == my_expectation 91 92 reality = src.telephone.text(int) 93 my_expectation = "I got: <class 'int'>" 94 assert reality == my_expectation 95 96 reality = src.telephone.text(float) 97 my_expectation = "I got: <class 'float'>" 98 assert reality == my_expectation 99 100 reality = src.telephone.text(str) 101 my_expectation = "I got: <class 'str'>" 102 assert reality == my_expectation 103 104 reality = src.telephone.text(tuple) 105 my_expectation = "I got: <class 'tuple'>" 106 assert reality == my_expectation 107 108 reality = src.telephone.text(list) 109 my_expectation = "I got: <class 'list'>" 110 assert reality == my_expectation 111 112 reality = src.telephone.text(set) 113 my_expectation = "I got: <class 'set'>" 114 assert reality == my_expectation 115 116 reality = src.telephone.text(dict) 117 my_expectation = "I got: <class 'dict'>" 118 assert reality == my_expectation 119 120 121# Exceptions seen 122# AssertionError 123# NameError 124# TypeError 125# ModuleNotFoundError 126# AttributeErrorI remove the
textfunction fromtest_telephone.py1import src.telephone 2 3 4def test_passing_none(): 5 reality = src.telephone.text(None) 6 my_expectation = 'I got: None' 7 assert reality == my_expectation 8 9 10def test_passing_booleans():all the tests are still green because all the calls that were to the
textfunction oftest_telephone.pyin thetestsfolder, now go to thetextfunction oftelephone.pyin thesrcfolder. Whensrc.telephone.textis called Python follows this pathsrc.telephone.text src └── telephone.py └── def text(the_input): └── return f'I got: {the_input}'I add a git commit message in the other terminal
git commit -am \ 'separate solution from tests'the terminal shows a summary of the changes then goes back to the command line.
test_telephone
Since the solution is separate from the tests, I can write the program that makes the tests pass without looking at test_telephone.py.
RED: make it fail
I close
test_telephone.pyI delete the text in
telephone.pyand the terminal shows 10 failures. I start with the last AttributeErrorFAILED ...::test_passing_none - AttributeError: module 'src.telephone' has no attribute 'text' FAILED ...::test_passing_booleans - AttributeError: module 'src.telephone' has no attribute 'text' FAILED ...::test_passing_an_integer - AttributeError: module 'src.telephone' has no attribute 'text' FAILED ...::test_passing_a_float - AttributeError: module 'src.telephone' has no attribute 'text' FAILED ...::test_passing_a_string - AttributeError: module 'src.telephone' has no attribute 'text' FAILED ...::test_passing_a_tuple - AttributeError: module 'src.telephone' has no attribute 'text' FAILED ...::test_passing_a_list - AttributeError: module 'src.telephone' has no attribute 'text' FAILED ...::test_passing_a_set - AttributeError: module 'src.telephone' has no attribute 'text' FAILED ...::test_passing_a_dictionary - AttributeError: module 'src.telephone' has no attribute 'text' FAILED ...::test_passing_a_class - AttributeError: module 'src.telephone' has no attribute 'text' =================== 10 failed in A.BCs ===================Can you make the tests pass without looking at how I solve it below? You can come back to compare solutions when you are done or if you get stuck.
GREEN: make it pass
I add the name to
telephone.py1textthe terminal is my friend, and shows NameError
NameError: name 'text' is not definedI point it to None (the simplest object) to define it
1# text 2text = Nonethe terminal is my friend, and shows TypeError
TypeError: 'NoneType' object is not callablebecause
textpoints to None and I cannot call None like a function.I make
texta function to make it callable1# text 2# text = None 3def text(): 4 return Nonethe terminal is my friend, and shows TypeError
TypeError: text() takes 0 positional arguments but 1 was givenbecause this function definition does not allow any inputs, the parentheses are empty.
I make the function take input
1# text 2# text = None 3# def text(): 4def text(value): 5 return Nonethe terminal is my friend, and shows AssertionError
E assert None == "I got: <class 'object'>"I copy the string from the terminal and paste it in the return statement to match the expectation of the test
1# text 2# text = None 3# def text(): 4def text(value): 5 # return None 6 return "I got: <class 'object'>"the terminal is my friend, and shows AssertionError
E assert "I got: <class 'object'>" == "I got: <class 'bool'>"I change the return statement to see the difference between the input and the expected output (remember the identity function?)
1# text 2# text = None 3# def text(): 4def text(value): 5 # return None 6 # return "I got: <class 'object'>" 7 return valuethe test summary info shows that every test has AssertionError
FAILED ...::test_passing_none - AssertionError: assert None == 'I got: None' FAILED ...::test_passing_booleans - AssertionError: assert False == 'I got: False' FAILED ...::test_passing_an_integer - AssertionError: assert 1234 == 'I got: 1234' FAILED ...::test_passing_a_float - AssertionError: assert 5.678 == 'I got: 5.678' FAILED ...::test_passing_a_string - AssertionError: assert 'hello' == 'I got: hello' FAILED ...::test_passing_a_tuple - assert (0, 1, 2, 'n') == "I got: (0, 1, 2, 'n')" FAILED ...::test_passing_a_list - assert [0, 1, 2, 'n'] == "I got: [0, 1, 2, 'n']" FAILED ...::test_passing_a_set - assert {0, 1, 2, 'n'} == "I got: {0, 1, 2, 'n'}" FAILED ...::test_passing_a_dictionary - assert {'key0': 'value0', 'keyN': [0, 1, 2, 'n']} == "I got: {'key0': 'value0',... FAILED ...::test_passing_a_class - assert <class 'object'> == "I got: <class 'object'>"they all expect the input (
value) as part of the messageI add a return statement with an f-string
1# text 2# text = None 3# def text(): 4def text(value): 5 # return None 6 # return "I got: <class 'object'>" 7 # return value 8 return f'I got: {value}'and all the tests are passing! I am a programmer!!
I remove the commented lines
1def text(value): 2 return f'I got: {value}'I add a git commit message in the other terminal
git commit --all --message \ 'test telephone'the terminal shows a summary of the changes then goes back to the command line.
close the project
I close
telephone.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
telephonecd ..the terminal shows
.../pumping_pythonI am back in the
pumping_pythondirectory.
review
code from the chapter
what is next?
would you like to separate the tests from the solution in the TypeError project?
rate pumping python
If this has been a 7 star experience for you, please CLICK HERE to leave a 5 star review of pumping python. It helps other people get into the book too.