test telephone with unittest
Part of Computer Programming is sending input data to a process and getting output data back
input_data -> process -> output_data
I send things (input data) to a program to test it, and check if what I think will happen (my expectation) is the same as the results I get (reality). This helps me answer two questions:
what is the same?
what is different?
The difference helps me know what to change to get what I want. I use assertions to test if the result of a call to a function with input is the same as my expectation.
assert reality == my_expectation
where
reality is what happens when I do something with code
my expectation is what I think will happen when I do something with code
The exercises in this chapter show how I can pass objects to a function and use it to make a string (anything in quotes). It will also show another way to organize tests.
preview
I have these tests by the end of the chapter
start the project
I name this project
telephoneI open a terminal
I use uv to make a directory for the project and initialize it
uv init telephonethe terminal shows
Initialized project `telephone` at `.../pumping_python/telephone`then goes back to the command line.
I change directory to the project
cd telephonethe terminal shows I am in the
telephonefolder.../pumping_python/telephoneI 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 use the mv program to change the name of
main.pytotest_telephone.pyand move it to thetestsfoldermv main.py tests/test_telephone.pyMove-Item main.py tests/test_telephone.pythe terminal goes back to the command line.
I open
test_telephone.pyI delete all the text then add the first failing test to
test_telephone.py1def test_failure(): 2 assert False is TrueI go back to the terminal to make a requirements file for the Python packages I need
echo "pytest" > requirements.txtthe terminal goes back to the command line.
I add pytest-watcher to the requirements file
echo "pytest-watcher" >> requirements.txtthe terminal goes back to the command line.
I use uv to install pytest-watcher with the requirements file
uv add --requirement requirements.txtthe terminal shows that it installed pytest-watcher and its dependencies.
I add the new files and folders to git for tracking
git add .the terminal goes back to the command line.
I add a git commit message
git commit --all --message 'setup project'the terminal shows
[main (root-commit) a0b12c3] setup project 8 files changed, X 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 tests/__init__.py create mode 100644 tests/test_telephone.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 ======================== ______________________ test_failure ______________________ def test_failure(): > assert False is True E assert False is True test_telephone.py:2: AssertionError ================ short test summary info ================= FAILED test_telephone.py::test_failure - assert False is True =================== 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
and try
uv run pytest-watcher . --nowagainI add AssertionError to the list of Exceptions seen in
test_telephone.py1def test_failure(): 2 assert False is True 3 4 5# Exceptions seen 6# AssertionErrorI change True to False in the assertion
1def test_failure(): 2 # assert False is True 3 assert False is False 4 5 6# Exceptions seen 7# AssertionErrorthe test passes.
test_passing_none
I can pass None (the simplest object) as input to a function?
RED: make it fail
I change
test_failureto test_passing_none with an assertion for a function call with None (the simplest Python data structure) as input, intest_telephone.py1def test_passing_none(): 2 assert text(None) == 'I got: None' 3 4 5# Exceptions seen 6# AssertionErrorthe terminal is my friend, and shows NameError
NameError: name 'text' is not definedbecause I have not defined the function yet.
I add NameError to the list of Exceptions seen
5# Exceptions seen 6# AssertionError 7# NameError
GREEN: make it pass
I add the function
1def test_passing_none(): 2 def text(): 3 return None 4 5 assert text(None) == 'I got: None' 6 7 8# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: test_passing_none.<locals>.text() takes 0 positional arguments but 1 was givenbecause the assertion called the
textfunction which belongs to test_passing_none with input (None) and the function definition does not allow any inputs, the parentheses are empty.I add TypeError to the list of Exceptions seen
8# Exceptions seen 9# AssertionError 10# NameError 11# TypeErrorI add a name to the function definition
1def test_passing_none(): 2 # def text(): 3 def text(the_input): 4 return None 5 6 assert text(None) == 'I got: None' 7 8 9# Exceptions seenthe_inputis the name I used for the input, I can use any name I want.the terminal is my friend, and shows AssertionError
E assert None == 'I got: None'
because the assertion expects
'I got: None'and thetextfunction returns None.I copy the string from the terminal and paste it in the return statement to replace None
1def test_passing_none(): 2 # def text(): 3 def text(the_input): 4 # return None 5 return 'I got: None' 6 7 assert text(None) == 'I got: None' 8 9 10# Exceptions seenthe test passes.
REFACTOR: make it better
I remove the commented lines
1def test_passing_none(): 2 def text(the_input): 3 return 'I got: None' 4 5 assert text(None) == 'I got: None' 6 7 8# Exceptions seenI open a new terminal then change directories to
telephonecd telephonethe terminal shows I am in the
telephonefolder.../pumping_python/telephoneI add a git commit message in the new terminal
git commit -am 'add test_passing_none'the terminal shows a summary of the changes then goes back to the command line.
I can pass None as input to a function.
The problem with this solution is that the text function does not care about what it gets, it always returns 'I got: None' when it is called. I want it to return the object it gets as part of the string.
test_passing_booleans
I can pass booleans from a test to a :ref:`function<what is a function?>.
RED: make it fail
I go back to the terminal where the tests are running
I add a test for booleans (there are only two), first with an assertion for False
18def test_passing_none(): 19 def text(the_input): 20 return 'I got: None' 21 22 assert text(None) == 'I got: None' 23 24 25def test_passing_booleans(): 26 assert text(False) == 'I got: False' 27 28 29# Exceptions seenthe terminal is my friend, and shows NameError
NameError: name 'text' is not definedbecause the
textfunction belongs to the test_passing_none function and I cannot reach it from outside.
GREEN: make it pass
I move the
textfunction out of test_passing_none so that it can be called from anywhere in the file1def text(the_input): 2 return 'I got: None' 3 4 5def test_passing_none(): 6 assert text(None) == 'I got: None' 7 8 9def test_passing_booleans():the terminal is my friend, and shows AssertionError
AssertionError: assert 'I got: None' == 'I got: False'because the
textfunction always returns'I got: None'and this assertion expects'I got: False'I change the return statement to give the test what it wants
1def text(the_input): 2 # return 'I got: None' 3 return 'I got: False' 4 5 6def test_passing_none():the terminal is my friend, and shows AssertionError
AssertionError: assert 'I got: False' == 'I got: None'because the
textfunction now always returns'I got: False'and the assertion in test_passing_none expects'I got: None'. My change broke the assertion that was passing before.The return statement has to use the input it gets as part of the output.
what is string interpolation?
`String Interpolation`_ is the placing of objects in strings. It allows me to make one string that can have changing values.
I can use an `f-string`_ (short for formatted string literal) for `string interpolation`_.
A string is anything inside quotes, for example
'single quotes''''triple single quotes'''"double quotes""""triple double quotes"""
how to write an f-string
f'characters {object}'
I change the return statement to an f-string
1def text(the_input): 2 # return 'I got: None' 3 # return 'I got: False' 4 return f'I got: {the_input}' 5 6 7def test_passing_none():the test passes because Python uses 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 'text(False) text(the_input) the_input = False return f'I got: {the_input}' return 'I got: False '
REFACTOR: make it better
I remove the commented lines
1def text(the_input): 2 return f'I got: {the_input}' 3 4 5def test_passing_none():I add an assertion for True (the other boolean) to test_passing_booleans
9def test_passing_booleans(): 10 assert text(False) == 'I got: False' 11 assert text(True) == 'I got: "True"' 12 13 14# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert 'I got: True' == 'I got: "True"I remove the quotes around True in my expectation
9def test_passing_booleans(): 10 assert text(False) == 'I got: False' 11 # assert text(True) == 'I got: "True"' 12 assert text(True) == 'I got: True' 13 14 15# Exceptions seenthe test passes because Python uses the string representation of the object in the curly braces
{ }text(True) text(the_input) the_input = True return f'I got: {the_input}' return 'I got: True 'I remove the commented line
9def test_passing_booleans(): 10 assert text(False) == 'I got: False' 11 assert text(True) == 'I got: True' 12 13 14# Exceptions seenI add a git commit message in the other terminal
git commit --all --message \ 'add test_passing_booleans'the terminal shows a summary of the changes then goes back to the command line.
test_passing_an_integer
Can I pass an integer (a whole number without decimals) as input to a function?.
RED: make it fail
I go back to the terminal where the tests are running
I add a test for an integer
9def test_passing_booleans(): 10 assert text(False) == 'I got: False' 11 assert text(True) == 'I got: True' 12 13 14def test_passing_an_integer(): 15 assert text(1234) == 'I got: "1234"' 16 17 18# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert 'I got: 1234' == 'I got: "1234"'
GREEN: make it pass
I remove the quotes around the integer in my expectation
14def test_passing_an_integer():
15 # assert text(1234) == 'I got: "1234"'
16 assert text(1234) == 'I got: 1234'
17
18
19# Exceptions seen
the test passes because Python uses the string representation of the object in the curly braces { }
text(1234)
text(the_input)
the_input = None
return f'I got: {the_input}'
return 'I got: 1234 '
REFACTOR: make it better
I add a variable for
123414def test_passing_an_integer(): 15 an_integer = 1234 16 # assert text(1234) == 'I got: "1234"' 17 assert text(1234) == 'I got: 1234' 18 19 20# Exceptions seenI use the variable and an f-string to remove repetition of
123414def test_passing_an_integer(): 15 an_integer = 1234 16 # assert text(1234) == 'I got: "1234"' 17 # assert text(1234) == 'I got: 1234' 18 assert text(an_integer) == f'I got: {an_integer}' 19 20 21# Exceptions seenthe test is still green.
I remove the commented lines
14def test_passing_an_integer(): 15 an_integer = 1234 16 assert text(an_integer) == f'I got: {an_integer}' 17 18 19# Exceptions seenI add a git commit message in the other terminal
git commit -am 'add test_passing_an_integer'the terminal shows a summary of the changes then goes back to the command line.
test_passing_a_float
Can I pass a float (binary floating point decimal number) as input to a function?.
RED: make it fail
I go back to the terminal where the tests are running
I add a test for a float (binary floating point decimal numbers)
14def test_passing_an_integer(): 15 an_integer = 1234 16 assert text(an_integer) == f'I got: {an_integer}' 17 18 19def test_passing_a_float(): 20 assert text(5.678) == 'I got: "5.678"' 21 22 23# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert 'I got: 5.678' == 'I got: "5.678"'
GREEN: make it pass
I remove the quotes around the float in my expectation
19def test_passing_a_float():
20 # assert text(5.678) == 'I got: "5.678"'
21 assert text(5.678) == 'I got: 5.678'
22
23
24# Exceptions seen
the test passes because Python uses the string representation of the object in the curly braces { }
text(5.678)
text(the_input)
the_input = 5.678
return f'I got: {the_input}'
return 'I got: 5.678 '
REFACTOR: make it better
I add a variable for
5.67819def test_passing_a_float(): 20 a_float = 5.678 21 # assert text(5.678) == 'I got: "5.678"' 22 assert text(5.678) == 'I got: 5.678' 23 24 25# Exceptions seenI use the variable and an f-string to remove repetition of
5.67819def test_passing_a_float(): 20 a_float = 5.678 21 # assert text(5.678) == 'I got: "5.678"' 22 # assert text(5.678) == 'I got: 5.678' 23 assert text(a_float) == f'I got: {a_float}' 24 25 26# Exceptions seenthe test is still green.
I remove the commented lines
19def test_passing_a_float(): 20 a_float = 5.678 21 assert text(a_float) == f'I got: {a_float}' 22 23 24# Exceptions seenI add a git commit message in the other terminal
git commit --all --message \ 'add test_passing_a_float'the terminal shows a summary of the changes then goes back to the command line.
test_passing_a_string
Can I pass a string as input to a function?.
RED: make it fail
I go back to the terminal where the tests are running
I add a test for a string (anything in quotes)
19def test_passing_a_float(): 20 a_float = 5.678 21 assert text(a_float) == f'I got: {a_float}' 22 23 24def test_passing_a_string(): 25 assert text('hi') == f'I got: hello' 26 27 28# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert 'I got: hi' == 'I got: hello'
GREEN: make it pass
I change my expectation to match reality
24def test_passing_a_string():
25 # assert text('hi') == f'I got: hello'
26 assert text('hi') == f'I got: hi'
27
28
29# Exceptions seen
the test passes because Python uses the string representation of the object in the curly braces { }
text(hi)
text(the_input)
the_input = hi
return f'I got: {the_input}'
return 'I got: hi '
REFACTOR: make it better
I add a variable for
'hi'24def test_passing_a_string(): 25 a_string = 'hi' 26 # assert text('hi') == f'I got: hello' 27 assert text('hi') == f'I got: hi' 28 29 30# Exceptions seenI use the variable and an f-string to remove repetition of
'hi'24def test_passing_a_string(): 25 a_string = 'hi' 26 # assert text('hi') == f'I got: hello' 27 # assert text('hi') == f'I got: hi' 28 assert text(a_string) == f'I got: {a_string}' 29 30 31# Exceptions seenthe test is still green.
I remove the commented lines
24def test_passing_a_string(): 25 a_string = 'hi' 26 assert text(a_string) == f'I got: {a_string}' 27 28 29# Exceptions seenI add a git commit message in the other terminal
git commit -am 'add test_passing_a_string'the terminal shows a summary of the changes then goes back to the command line.
test_passing_a_tuple
Can I pass a tuple (anything in parentheses ( ) separated by a comma) as input to a function?
RED: make it fail
I go back to the terminal where the tests are running
I add a test for a tuple
24def test_passing_a_string(): 25 a_string = 'hi' 26 assert text(a_string) == f'I got: {a_string}' 27 28 29def test_passing_a_tuple(): 30 assert text((0, 1, 2, 'n')) == 'I got: (1, 2, 3, n)' 31 32 33# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert "I got: (0, 1, 2, 'n')" == 'I got: (1, 2, 3, n)'
GREEN: make it pass
I change the tuple in my expectation to match reality
29def test_passing_a_tuple():
30 # assert text((0, 1, 2, 'n')) == 'I got: (1, 2, 3, n)'
31 assert text((0, 1, 2, 'n')) == "I got: (0, 1, 2, 'n')"
32
33
34# Exceptions seen
the test passes because Python uses the string representation of the object in the curly braces { }
text((0, 1, 2, 'n'))
text(the_input)
the_input = (0, 1, 2, 'n')
return f'I got: {the_input }'
return 'I got: (0, 1, 2, 'n')'
REFACTOR: make it better
I add a variable for
(0, 1, 2, 'n')29def test_passing_a_tuple(): 30 a_tuple = (0, 1, 2, 'n') 31 # assert text((0, 1, 2, 'n')) == 'I got: (0, 1, 2, 'n')' 32 assert text((0, 1, 2, 'n')) == "I got: (0, 1, 2, 'n')" 33 34 35# Exceptions seenI use the variable and an f-string to remove repetition of
(0, 1, 2, 'n')29def test_passing_a_tuple(): 30 a_tuple = (0, 1, 2, 'n') 31 # assert text((0, 1, 2, 'n')) == 'I got: (0, 1, 2, 'n')' 32 # assert text((0, 1, 2, 'n')) == "I got: (0, 1, 2, 'n')" 33 assert text(a_tuple) == f'I got: {a_tuple}' 34 35 36# Exceptions seenthe test is still green.
I remove the commented lines
29def test_passing_a_tuple(): 30 a_tuple = (0, 1, 2, 'n') 31 assert text(a_tuple) == f'I got: {a_tuple}' 32 33 34# Exceptions seenI add a git commit message in the other terminal
git commit --all --message 'add test_passing_a_tuple'the terminal shows a summary of the changes then goes back to the command line.
test_passing_a_list
Can I pass a list (anything in square brackets [ ]) from a test to a function?
RED: make it fail
I go back to the terminal where the tests are running
I add a test for a list
29def test_passing_a_tuple(): 30 a_tuple = (0, 1, 2, 'n') 31 assert text(a_tuple) == f'I got: {a_tuple}' 32 33 34def test_passing_a_list(): 35 assert text([0, 1, 2, 'n']) == 'I got: [0, 1, 2, "n"]' 36 37 38# Exceptions seenthe terminal is my friend, and shows AssertionError
assert "I got: [0, 1, 2, 'n']" == 'I got: [0, 1, 2, "n"]'
GREEN: make it pass
I change the list in my expectation to match reality
34def test_passing_a_list():
35 # assert text([0, 1, 2, 'n']) == 'I got: [0, 1, 2, "n"]'
36 assert text([0, 1, 2, 'n']) == "I got: [0, 1, 2, 'n']"
37
38
39# Exceptions seen
the test passes because Python uses the string representation of the object in the curly braces { }
text([0, 1, 2, 'n'])
text(the_input)
the_input = [0, 1, 2, 'n']
return f'I got: {the_input }'
return 'I got: [0, 1, 2, 'n']'
Python changed the double quotes (") in the list to a single quote (').
REFACTOR: make it better
I add a variable for
[0, 1, 2, 'n']34def test_passing_a_list(): 35 a_list = [0, 1, 2, 'n'] 36 # assert text([0, 1, 2, 'n']) == 'I got: [0, 1, 2, "n"]' 37 assert text([0, 1, 2, 'n']) == "I got: [0, 1, 2, 'n']" 38 39 40# Exceptions seenI use the variable and an f-string to remove repetition of
[0, 1, 2, 'n']34def test_passing_a_list(): 35 a_list = [0, 1, 2, 'n'] 36 # assert text([0, 1, 2, 'n']) == 'I got: [0, 1, 2, "n"]' 37 # assert text([0, 1, 2, 'n']) == "I got: [0, 1, 2, 'n']" 38 assert text(a_list) == f'I got: {a_list}' 39 40 41# Exceptions seenthe test is still green.
I remove the commented lines
50def test_passing_a_list(): 51 a_list = [0, 1, 2, 'n'] 52 assert text(a_list) == f'I got: {a_list}' 53 54 55# Exceptions seenI add a git commit message in the other terminal
git commit -am 'add test_passing_a_list'the terminal shows a summary of the changes then goes back to the command line.
test_passing_a_set
Can I pass a set (anything in curly braces { }, not key-value pairs) from a test to a function?
RED: make it fail
I go back to the terminal where the tests are running
I add a test for a set
34def test_passing_a_list(): 35 a_list = [0, 1, 2, 'n'] 36 assert text(a_list) == f'I got: {a_list}' 37 38 39def test_passing_a_set(): 40 assert text({0, 1, 2, 'n'}) == 'I got: {0, 1, 2, "n"}' 41 42 43# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert "I got: {0, 1, 2, 'n'}" == 'I got: {0, 1, 2, "n"}'
GREEN: make it pass
I change the set in my expectation to match reality
39def test_passing_a_set(): 40 # assert text({0, 1, 2, 'n'}) == 'I got: {0, 1, 2, "n"}' 41 assert text({0, 1, 2, 'n'}) == "I got: {0, 1, 2, 'n'}" 42 43 44# Exceptions seenI use ctrl/command+s (Windows & Linux/MacOS) to run the test a few times
if the result of
text({0, 1, 2, 'n'})is equal to"I got: {0, 1, 2, 'n'}"the test passes because Python uses the string representation of the object in the curly braces{ }text({0, 1, 2, 'n'}) text(the_input) the_input = {0, 1, 2, 'n'} return f'I got: {the_input }' return 'I got: {0, 1, 2, 'n'}'if the result of
text({0, 1, 2, 'n'})is NOT equal to"I got: {0, 1, 2, 'n'}", the terminal shows AssertionErrorE assert "I got: {0, 'n', 2, 1}" == "I got: {0, 1, 2, 'n'}"
Python cannot guarantee the order of the things in the set and the order matters for the assertion that is comparing the strings because
these two are the same set
{0, 'n', 2, 1} == {0, 1, 2, 'n'}these two are not the same string
"{0, 'n', 2, 1}" != "{0, 1, 2, 'n'}"
I add a variable for
{0, 1, 2, 'n'}39def test_passing_a_set(): 40 a_set = {0, 1, 2, 'n'} 41 # assert text({0, 1, 2, 'n'}) == 'I got: {0, 1, 2, "n"}' 42 assert text({0, 1, 2, 'n'}) == "I got: {0, 1, 2, 'n'}" 43 44 45# Exceptions seenI use the variable and an f-string to remove repetition of
{0, 1, 2, 'n'}34def test_passing_a_set(): 35 a_set = {0, 1, 2, 'n'} 36 # assert text({0, 1, 2, 'n'}) == 'I got: {0, 1, 2, "n"}' 37 # assert text({0, 1, 2, 'n'}) == "I got: {0, 1, 2, 'n'}" 38 assert text(a_set) == f'I got: {a_set}' 39 40 41# Exceptions seenI use ctrl/command+s (Windows & Linux/MacOS) to run the test a few times and the test stays green with no random failures because Python uses the string representation of the object in the curly braces
{ }.It can guarantee the order when I use a variable and the f-string to refer to the same exact set.
REFACTOR: make it better
I remove the commented lines
50def test_passing_a_set(): 51 a_set = {0, 1, 2, 'n'} 52 assert text(a_set) == f'I got: {a_set}' 53 54 55# Exceptions seenI add a git commit message in the other terminal
git commit --all --message 'add test_passing_a_set'the terminal shows a summary of the changes then goes back to the command line.
test_passing_a_dictionary
Can I pass a dictionary (any key-value pairs in curly braces ‘{ }’ separated by commas as input to a function?
RED: make it fail
I go back to the terminal where the tests are running
I add a test for a dictionary
39def test_passing_a_set(): 40 a_set = {0, 1, 2, 'n'} 41 assert text(a_set) == f'I got: {a_set}' 42 43 44def test_passing_a_dictionary(): 45 reality = text({ 46 'key0': 'value0', 47 'keyN': [0, 1, 2, 'n'], 48 }) 49 my_expectation = ( 50 "I got: " 51 "{key0: value0, keyN: [0, 1, 2, n]}" 52 ) 53 assert reality == my_expectation 54 55 56# Exceptions seenthe terminal is my friend, and shows AssertionError
assert "I got: {'key..., 1, 2, 'n']}" == 'I got: {key0...[0, 1, 2, n]}'
GREEN: make it pass
I change my_expectation to match reality
44def test_passing_a_dictionary():
45 reality = text({
46 'key0': 'value0',
47 'keyN': [0, 1, 2, 'n'],
48 })
49 my_expectation = (
50 "I got: "
51 # "{key0: value0, keyN: [0, 1, 2, n]}"
52 "{'key0': 'value0', 'keyN': [0, 1, 2, 'n']}"
53 )
54 assert reality == my_expectation
55
56
57# Exceptions seen
the test passes because Python uses the string representation of the object in the curly braces { }
text({'key0': 'value0', 'keyN': [0, 1, 2, 'n'],})
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']}"
REFACTOR: make it better
I add a variable for
{'key0': 'value0', 'keyN': [0, 1, 2, 'n'],}44def test_passing_a_dictionary(): 45 a_dictionary = { 46 'key0': 'value0', 47 'keyN': [0, 1, 2, 'n'], 48 } 49 reality = text({ 50 'key0': 'value0', 51 'keyN': [0, 1, 2, 'n'], 52 }) 53 my_expectation = ( 54 "I got: " 55 # "{key0: value0, keyN: [0, 1, 2, n]}" 56 "{'key0': 'value0', 'keyN': [0, 1, 2, 'n']}" 57 ) 58 assert reality == my_expectation 59 60 61# Exceptions seenI use the variable and an f-string to remove repetition of
{'key0': 'value0', 'keyN': [0, 1, 2, 'n'],}44def test_passing_a_dictionary(): 45 a_dictionary = { 46 'key0': 'value0', 47 'keyN': [0, 1, 2, 'n'], 48 } 49 # reality = text({ 50 # 'key0': 'value0', 51 # 'keyN': [0, 1, 2, 'n'], 52 # }) 53 # my_expectation = ( 54 # "I got: " 55 # # "{key0: value0, keyN: [0, 1, 2, n]}" 56 # "{'key0': 'value0', 'keyN': [0, 1, 2, 'n']}" 57 # ) 58 reality = text(a_dictionary) 59 my_expectation = f'I got: {a_dictionary}' 60 assert reality == my_expectation 61 62 63# Exceptions seenthe test is still green.
I remove the commented lines
56def test_passing_a_dictionary(): 57 a_dictionary = { 58 'key0': 'value0', 59 'keyN': [0, 1, 2, 'n'], 60 } 61 reality = text(a_dictionary) 62 my_expectation = f'I got: {a_dictionary}' 63 assert reality == my_expectation 64 65 66# Exceptions seenI add a git commit message in the other terminal
git commit -am 'add test_passing_a_dictionary'the terminal shows a summary of the changes then goes back to the command line.
test_passing_a_class
Can I pass any object as input to a function?
RED: make it fail
I go back to the terminal where the tests are running
I add a failing test to see what happens when I pass a class from a test to the
textfunction, intest_telephone.py44def test_passing_a_dictionary(): 45 a_dictionary = { 46 'key0': 'value0', 47 'keyN': [0, 1, 2, 'n'], 48 } 49 reality = text(a_dictionary) 50 my_expectation = f'I got: {a_dictionary}' 51 assert reality == my_expectation 52 53 54def test_passing_a_class(): 55 assert text(object) == 'I got: object' 56 57 58# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert "I got: <class 'object'>" == 'I got: object'object is the mother class that all Python classes come from, and everything in Python is an object.
GREEN: make it pass
I change my expectation to match reality
54def test_passing_a_class():
55 # assert text(object) == 'I got: object'
56 assert text(object) == "I got: <class 'object'>"
57
58
59# Exceptions seen
the test passes because Python uses the string representation of the object in the curly braces { }
text(object)
text(the_input)
the_input = object
return f'I got: {the_input }'
return "I got: <class 'object'> "
REFACTOR: make it better
I add an assertion for bool (the class for booleans)
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 assert text(bool) == 'I got: bool' 58 59 60# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert "I got: <class 'bool'>" == 'I got: bool'I change my expectation to match reality
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 60 61# Exceptions seenthe test passes.
I add an assertion for int (the class for whole numbers without decimals)
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 assert text(int) == 'I got: int' 60 61 62# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert "I got: <class 'int'>" == 'I got: int'I change my expectation to match reality
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 # assert text(int) == "I got: int" 60 assert text(int) == "I got: <class 'int'>" 61 62 63# Exceptions seenthe test passes.
I add an assertion for float (the class for binary floating point decimal numbers)
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 # assert text(int) == "I got: int" 60 assert text(int) == "I got: <class 'int'>" 61 assert text(float) == 'I got: float' 62 63 64# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert "I got: <class 'float'>" == 'I got: float'I change my expectation to match reality
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 # assert text(int) == "I got: int" 60 assert text(int) == "I got: <class 'int'>" 61 # assert text(float) == 'I got: float' 62 assert text(float) == "I got: <class 'float'>" 63 64 65# Exceptions seenthe test passes.
I add an assertion for str (the class for anything in quotes)
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 # assert text(int) == "I got: int" 60 assert text(int) == "I got: <class 'int'>" 61 # assert text(float) == 'I got: float' 62 assert text(float) == "I got: <class 'float'>" 63 assert text(str) == 'I got: str' 64 65 66# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert "I got: <class 'str'>" == 'I got: str'I change my expectation to match reality
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 # assert text(int) == "I got: int" 60 assert text(int) == "I got: <class 'int'>" 61 # assert text(float) == 'I got: float' 62 assert text(float) == "I got: <class 'float'>" 63 # assert text(str) == 'I got: str' 64 assert text(str) == "I got: <class 'str'>" 65 66 67# Exceptions seenthe test passes.
I add an assertion for tuple (the class for anything in parentheses
( )separated by a comma)54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 # assert text(int) == "I got: int" 60 assert text(int) == "I got: <class 'int'>" 61 # assert text(float) == 'I got: float' 62 assert text(float) == "I got: <class 'float'>" 63 # assert text(str) == 'I got: str' 64 assert text(str) == "I got: <class 'str'>" 65 assert text(tuple) == 'I got: tuple' 66 67 68# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert "I got: <class 'tuple'>" == 'I got: tuple'I change my expectation to match reality
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 # assert text(int) == "I got: int" 60 assert text(int) == "I got: <class 'int'>" 61 # assert text(float) == 'I got: float' 62 assert text(float) == "I got: <class 'float'>" 63 # assert text(str) == 'I got: str' 64 assert text(str) == "I got: <class 'str'>" 65 # assert text(tuple) == 'I got: tuple' 66 assert text(tuple) == "I got: <class 'tuple'>" 67 68 69# Exceptions seenthe test passes.
I add an assertion for list (the class for anything in square brackets ‘[ ]’)
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 # assert text(int) == "I got: int" 60 assert text(int) == "I got: <class 'int'>" 61 # assert text(float) == 'I got: float' 62 assert text(float) == "I got: <class 'float'>" 63 # assert text(str) == 'I got: str' 64 assert text(str) == "I got: <class 'str'>" 65 # assert text(tuple) == 'I got: tuple' 66 assert text(tuple) == "I got: <class 'tuple'>" 67 assert text(list) == 'I got: list' 68 69 70# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert "I got: <class 'tuple'>" == 'I got: tuple'I change my expectation to match reality
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 # assert text(int) == "I got: int" 60 assert text(int) == "I got: <class 'int'>" 61 # assert text(float) == 'I got: float' 62 assert text(float) == "I got: <class 'float'>" 63 # assert text(str) == 'I got: str' 64 assert text(str) == "I got: <class 'str'>" 65 # assert text(tuple) == 'I got: tuple' 66 assert text(tuple) == "I got: <class 'tuple'>" 67 # assert text(list) == 'I got: list' 68 assert text(list) == "I got: <class 'list'>" 69 70 71# Exceptions seenthe test passes.
I add an assertion for set (the class anything in curly braces
{ }, not key-value pairs)54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 # assert text(int) == "I got: int" 60 assert text(int) == "I got: <class 'int'>" 61 # assert text(float) == 'I got: float' 62 assert text(float) == "I got: <class 'float'>" 63 # assert text(str) == 'I got: str' 64 assert text(str) == "I got: <class 'str'>" 65 # assert text(tuple) == 'I got: tuple' 66 assert text(tuple) == "I got: <class 'tuple'>" 67 # assert text(list) == 'I got: list' 68 assert text(list) == "I got: <class 'list'>" 69 assert text(set) == 'I got: set' 70 71 72# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert "I got: <class 'set'>" == 'I got: set'I change my expectation to match reality
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 # assert text(int) == "I got: int" 60 assert text(int) == "I got: <class 'int'>" 61 # assert text(float) == 'I got: float' 62 assert text(float) == "I got: <class 'float'>" 63 # assert text(str) == 'I got: str' 64 assert text(str) == "I got: <class 'str'>" 65 # assert text(tuple) == 'I got: tuple' 66 assert text(tuple) == "I got: <class 'tuple'>" 67 # assert text(list) == 'I got: list' 68 assert text(list) == "I got: <class 'list'>" 69 # assert text(set) == 'I got: set' 70 assert text(set) == "I got: <class 'set'>" 71 72 73# Exceptions seenthe test passes.
I add an assertion for dict (the class for key-value pairs in curly braces ‘{ }’ separated by commas)
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 # assert text(int) == "I got: int" 60 assert text(int) == "I got: <class 'int'>" 61 # assert text(float) == 'I got: float' 62 assert text(float) == "I got: <class 'float'>" 63 # assert text(str) == 'I got: str' 64 assert text(str) == "I got: <class 'str'>" 65 # assert text(tuple) == 'I got: tuple' 66 assert text(tuple) == "I got: <class 'tuple'>" 67 # assert text(list) == 'I got: list' 68 assert text(list) == "I got: <class 'list'>" 69 # assert text(set) == 'I got: set' 70 assert text(set) == "I got: <class 'set'>" 71 assert text(dict) == 'I got: dict' 72 73 74# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert "I got: <class 'dict'>" == 'I got: dict'I change my expectation to match reality
54def test_passing_a_class(): 55 # assert text(object) == 'I got: object' 56 assert text(object) == "I got: <class 'object'>" 57 # assert text(bool) == 'I got: bool' 58 assert text(bool) == "I got: <class 'bool'>" 59 # assert text(int) == "I got: int" 60 assert text(int) == "I got: <class 'int'>" 61 # assert text(float) == 'I got: float' 62 assert text(float) == "I got: <class 'float'>" 63 # assert text(str) == 'I got: str' 64 assert text(str) == "I got: <class 'str'>" 65 # assert text(tuple) == 'I got: tuple' 66 assert text(tuple) == "I got: <class 'tuple'>" 67 # assert text(list) == 'I got: list' 68 assert text(list) == "I got: <class 'list'>" 69 # assert text(set) == 'I got: set' 70 assert text(set) == "I got: <class 'set'>" 71 # assert text(dict) == 'I got: dict' 72 assert text(dict) == "I got: <class 'dict'>" 73 74 75# Exceptions seenthe test passes because Python uses the string representation of the object in the curly braces
{ }text(dict) text(the_input) the_input = dict return f'I got: {the_input }' return "I got: <class 'dict'> "I remove the commented lines
54def test_passing_a_class(): 55 assert text(object) == "I got: <class 'object'>" 56 assert text(bool) == "I got: <class 'bool'>" 57 assert text(int) == "I got: <class 'int'>" 58 assert text(float) == "I got: <class 'float'>" 59 assert text(str) == "I got: <class 'str'>" 60 assert text(tuple) == "I got: <class 'tuple'>" 61 assert text(list) == "I got: <class 'list'>" 62 assert text(set) == "I got: <class 'set'>" 63 assert text(dict) == "I got: <class 'dict'>" 64 65 66# Exceptions seenI add a git commit message in the other terminal
git commit --all --message 'add test_passing_a_class'the terminal shows a summary of the changes then goes back to the command line.
separate and equal
So far all functions I have written have been in the same file as the tests, some are even in the same function as the assertions of the tests.
In earlier tests I found it better to keep functions outside of functions so that anything could call them from outside.
I can also place them in other modules then use the import statement to bring in the function so I can test it. This helps me keep tests and solutions separate. It also means I can send tests only, solutions only or both.
RED: make it fail
I go back to the terminal where the tests are running
I change the assertion to call the
textfunction of thetelephonemodule in thesrcfolder1def text(the_input): 2 return f'I got: {the_input}' 3 4 5def test_passing_none(): 6 # assert text(None) == 'I got: None' 7 reality = src.telephone.text(None) 8 my_expectation = '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 definedbecause 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):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
73# Exceptions seen 74# AssertionError 75# NameError 76# TypeError 77# ModuleNotFoundErrorI go to the other terminal
I 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
73# Exceptions seen 74# AssertionError 75# NameError 76# TypeError 77# ModuleNotFoundError 78# 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 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.pyfrom thesrcfolderI add a copy of the
textfunction totelephone.py1def text(the_input): 2 return f'I got: {the_input}'the test passes because
when
import src.telephoneruns, Python brings in an object for thetelephone.pyfile from thesrcfolder so I can use it intest_telephone.pyassrc.telephonewhen
src.telephone.text(None)runs, Python calls thetextfunction from the object it imported for thetelephone.pyfile from thesrcfolder (src.telephone)
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 # assert text(False) == 'I got: False' 16 reality = src.telephone.text(False) 17 my_expectation = '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}'then uses 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 # assert text(True) == 'I got: True' 20 reality = src.telephone.text(True) 21 my_expectation = 'I got: True' 22 assert reality == my_expectation 23 24 25def 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 # assert text(an_integer) == f'I got: {an_integer}' 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():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 # assert text(a_float) == f'I got: {a_float}' 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():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}'then uses 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 = 'hi' 42 # assert text(a_string) == f'I got: {a_string}' 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 remove the commented line
40def test_passing_a_string(): 41 a_string = 'hi' 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 # assert text(a_tuple) == f'I got: {a_tuple}' 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():still 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 # assert text(a_list) == f'I got: {a_list}' 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():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 # assert text(a_set) == f'I got: {a_set}' 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():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 call in
realityfor test_passing_a_dictionary72def 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():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}'then uses 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 # assert text(object) == "I got: <class 'object'>" 85 reality = src.telephone.text(object) 86 my_expectation = "I got: <class 'object'>" 87 assert reality == my_expectation 88 89 # assert text(bool) == "I got: <class 'bool'>" 90 reality = src.telephone.text(bool) 91 my_expectation = "I got: <class 'bool'>" 92 assert reality == my_expectation 93 94 # assert text(int) == "I got: <class 'int'>" 95 reality = src.telephone.text(int) 96 my_expectation = "I got: <class 'int'>" 97 assert reality == my_expectation 98 99 # assert text(float) == "I got: <class 'float'>" 100 reality = src.telephone.text(float) 101 my_expectation = "I got: <class 'float'>" 102 assert reality == my_expectation 103 104 # assert text(str) == "I got: <class 'str'>" 105 reality = src.telephone.text(str) 106 my_expectation = "I got: <class 'str'>" 107 assert reality == my_expectation 108 109 # assert text(tuple) == "I got: <class 'tuple'>" 110 reality = src.telephone.text(tuple) 111 my_expectation = "I got: <class 'tuple'>" 112 assert reality == my_expectation 113 114 # assert text(list) == "I got: <class 'list'>" 115 reality = src.telephone.text(list) 116 my_expectation = "I got: <class 'list'>" 117 assert reality == my_expectation 118 119 # assert text(set) == "I got: <class 'set'>" 120 reality = src.telephone.text(set) 121 my_expectation = "I got: <class 'set'>" 122 assert reality == my_expectation 123 124 # assert text(dict) == "I got: <class 'dict'>" 125 reality = src.telephone.text(dict) 126 my_expectation = "I got: <class 'dict'>" 127 assert reality == my_expectation 128 129 130# Exceptions seenthe test is still green.
I remove the commented lines
I 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 to the
textfunction that was intest_telephone.pyare now to thetextfunction intelephone.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
Now that 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 9 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 'hi' == 'I got: hi' 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
I can use the unittest library to write tests with the methods of the unittest.TestCase class or I can write them with bare assert statements.
code from the chapter
what is next?
You now 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.