test functions with generators
I want to use for loops in the functions project.
preview
I have these tests by the end of the chapter
1import src.functions
2import unittest
3
4
5class TestFunctions(unittest.TestCase):
6
7 first = 'first'
8 last = 'last'
9 a_tuple = (0, 1, 2, 'n')
10 a_list = [0, 1, 2, 'n']
11 a_set = {0, 1, 2, 'n'}
12 a_dictionary = {'key': 'value'}
14 def test_functions_return_none_by_default(self):
15 for a_function in (
16 src.functions.w_pass,
17 src.functions.w_return,
18 src.functions.w_return_none,
19 src.functions.return_leaves_the_function,
20 ):
21 with self.subTest(a_function):
22 self.assertIsNone(a_function())
23
24 def test_constant_function(self):
25 self.assertEqual(
26 src.functions.constant(), 'the same thing'
27 )
29 def test_identity_function(self):
30 for an_object in (
31 None, object, self.first, self.last,
32 self.a_tuple, self.a_list,
33 self.a_set, self.a_dictionary,
34 ):
35 with self.subTest(an_object):
36 self.assertEqual(
37 src.functions.identity(an_object),
38 an_object
39 )
41 def test_why_use_a_for_loop(self):
42 def add_x(number):
43 return 3 + number
44
45 for a_number in range(1000):
46 with self.subTest(a_number):
47 self.assertEqual(
48 add_x(a_number), a_number+3
49 )
51 def test_args_and_kwargs(self):
52 for first, second in (
53 (self.first, self.last),
54 (self.last, self.first),
55 (0, 1),
56 (self.a_tuple, self.a_list),
57 (self.a_set, self.a_dictionary),
58 ):
59 for a_function in (
60 src.functions.positional_arguments,
61 src.functions.keyword_arguments,
62 src.functions.args_and_kwargs,
63 src.functions.optional_arguments,
64 ):
65 with self.subTest(
66 a_function=a_function, inputs=(first, second)
67 ):
68 self.assertEqual(
69 a_function(first, second),
70 (first, second)
71 )
72 self.assertEqual(
73 a_function(
74 last_input=first,
75 first_input=second,
76 ),
77 (second, first)
78 )
79 self.assertEqual(
80 a_function(
81 first, last_input=second,
82 ),
83 (first, second)
84 )
85 self.assertEqual(
86 a_function(
87 second, last_input=first,
88 ),
89 (second, first)
90 )
open the project
I open a terminal
I change directory to the project
I open
test_functions.pyfrom thetestsfolderI use pytest-watcher to run the tests automatically
extract test_functions_return_none_by_default
test_making_a_function_w_pass, test_making_a_function_w_return, test_making_a_function_w_return_none and test_what_happens_after_functions_return all assert that a function returns None by default
self.assertIsNone(function())
I can make a function with a for loop to remove the repetition.
RED: make it fail
I add a test with a for loop for what a function returns by default
10 a_list = [0, 1, 2, 'n']
11 a_set = {0, 1, 2, 'n'}
12 a_dictionary = {'key': 'value'}
13
14 def test_functions_return_none_by_default(self):
15 for a_function in (
16 src.functions.w_pass,
17 src.functions.w_return,
18 src.functions.w_return_none,
19 src.functions.return_leaves_the_function,
20 ):
21 with self.subTest(a_function):
22 self.assertIsNotNone(a_function())
23
24 def test_making_a_function_w_pass(self):
the terminal is my friend, and shows AssertionError
SUBFAILED[<function w_pass at 0xffffa0b12345>] ...
AssertionError: unexpectedly None
SUBFAILED[<function w_return at 0xffffc6d78012>] ...
AssertionError: unexpectedly None
SUBFAILED[<function w_return_none at 0xffffe3f45678>] ...
AssertionError: unexpectedly None
SUBFAILED[<function return_leaves_the_function at 0xffffa01234b5>] ...
AssertionError: unexpectedly None
GREEN: make it pass
I change assertIsNotNone to assertIsNone
21 with self.subTest(a_function):
22 self.assertIsNone(a_function())
23
24 def test_making_a_function_w_pass(self):
the test passes
REFACTOR: make it better
I remove test_making_a_function_w_pass because it is now covered by test_functions_return_none_by_default
I remove test_making_a_function_w_return because it is now covered by test_functions_return_none_by_default
I remove test_making_a_function_w_return_none because it is now covered by test_functions_return_none_by_default
I remove test_what_happens_after_functions_return because it is now covered by test_functions_return_none_by_default
I add a git commit message
test_identity_function with a loop
RED: make it fail
I add a for loop to test_identity_function
29 def test_identity_function(self):
30 for an_object in (
31 None, object, self.first, self.last,
32 self.a_tuple, self.a_list,
33 self.a_set, self.a_dictionary,
34 ):
35 with self.subTest(an_object):
36 self.assertNotEqual(
37 src.functions.identity(an_object),
38 an_object
39 )
40
41 self.assertIsNone(src.functions.identity(None))
42 self.assertEqual(
43 src.functions.identity(object), object
44 )
45
46 def test_why_use_a_function(self):
the terminal is my friend, and shows AssertionError
SUBFAILED[None] ...AssertionError: None == None
SUBFAILED[<class 'object'>] ...
AssertionError: <class 'object'> == <class 'object'>
SUBFAILED[first] ...AssertionError: 'first' == 'first'
SUBFAILED[last] ...AssertionError: 'last' == 'last'
SUBFAILED[(0, 1, 2, 'n')] ...
AssertionError: (0, 1, 2, 'n') == (0, 1, 2, 'n')
SUBFAILED[[0, 1, 2, 'n']] ...
AssertionError: [0, 1, 2, 'n'] == [0, 1, 2, 'n']
SUBFAILED[{0, 1, 2, 'n'}] ...
AssertionError: {0, 1, 2, 'n'} == {0, 1, 2, 'n'}
SUBFAILED[{'key': 'value'}] ...
AssertionError: {'key': 'value'} == {'key': 'value'}
GREEN: make it pass
I change assertNotEqual to assertEqual
35 with self.subTest(an_object):
36 self.assertEqual(
37 src.functions.identity(an_object),
38 an_object
39 )
40
41 self.assertIsNone(src.functions.identity(None))
the test passes.
REFACTOR: make it better
I remove the other assertions from test_identity_function because they are now covered by the for loop
29 def test_identity_function(self): 30 for an_object in ( 31 None, object, self.first, self.last, 32 self.a_tuple, self.a_list, 33 self.a_set, self.a_dictionary, 34 ): 35 with self.subTest(an_object): 36 self.assertEqual( 37 src.functions.identity(an_object), 38 an_object 39 ) 40 41 def test_why_use_a_function(self):I add a git commit message
test_why_use_a_for_loop
test_why_use_a_function makes ten assertions that all look the same
self.assertEqual(add_x(a_number), result)
I can use a for loop to remove the repetition
RED: make it fail
I add a for loop to test_why_use_a_function
41 def test_why_use_a_function(self):
42 def add_x(number):
43 return 3 + number
44
45 for a_number in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9):
46 with self.subTest(a_number):
47 self.assertEqual(add_x(a_number), 'BOOM!!!')
48
49 self.assertEqual(add_x(0), 3)
50 self.assertEqual(add_x(1), 4)
51 self.assertEqual(add_x(2), 5)
the terminal is my friend, and shows AssertionError
SUBFAILED[0] ...AssertionError: 3 != 'BOOM!!!'
SUBFAILED[1] ...AssertionError: 4 != 'BOOM!!!'
SUBFAILED[2] ...AssertionError: 5 != 'BOOM!!!'
SUBFAILED[3] ...AssertionError: 6 != 'BOOM!!!'
SUBFAILED[4] ...AssertionError: 7 != 'BOOM!!!'
SUBFAILED[5] ...AssertionError: 8 != 'BOOM!!!'
SUBFAILED[6] ...AssertionError: 9 != 'BOOM!!!'
SUBFAILED[7] ...AssertionError: 10 != 'BOOM!!!'
SUBFAILED[8] ...AssertionError: 11 != 'BOOM!!!'
SUBFAILED[9] ...AssertionError: 12 != 'BOOM!!!'
GREEN: make it pass
I change self.assertEqual(add_x(a_number), 'BOOM!!!')
45 for a_number in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9):
46 with self.subTest(a_number):
47 self.assertEqual(
48 add_x(a_number), a_number+3
49 )
50
51 self.assertEqual(add_x(0), 3)
the test passes.
REFACTOR: make it better
I remove the other assertions from test_why_use_a_function because they are now covered by the for loop
41 def test_why_use_a_function(self): 42 def add_x(number): 43 return 3 + number 44 45 for a_number in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9): 46 with self.subTest(a_number): 47 self.assertEqual( 48 add_x(a_number), a_number+3 49 ) 50 51 def test_positional_arguments(self):
the range object
Python has an iterable I can use to make a sequence of numbers, it is called the range object.
range(0, a_number) makes a range object that goes from the first number in the parentheses to the second number minus 1
I change the tuple in the for loop to a range object
41 def test_why_use_a_function(self): 42 def add_x(number): 43 return 3 + number 44 45 for a_number in range(0, 10): 46 with self.subTest(a_number): 47 self.assertNotEqual( 48 add_x(a_number), a_number+3 49 ) 50 51 def test_positional_arguments(self):the terminal is my friend, and shows AssertionError
SUBFAILED[0] ...AssertionError: 3 == 3 SUBFAILED[1] ...AssertionError: 4 == 4 SUBFAILED[2] ...AssertionError: 5 == 5 SUBFAILED[3] ...AssertionError: 6 == 6 SUBFAILED[4] ...AssertionError: 7 == 7 SUBFAILED[5] ...AssertionError: 8 == 8 SUBFAILED[6] ...AssertionError: 9 == 9 SUBFAILED[7] ...AssertionError: 10 == 10 SUBFAILED[8] ...AssertionError: 11 == 11 SUBFAILED[9] ...AssertionError: 12 == 12I can remove the
0from the range objectthe AssertionError stays the same.
I can use any number I want
the terminal shows AssertionError for one thousand sub tests and my tests run slower than before. What would happen if I changed the number to one million?
I change assertNotEqual to assertEqual
41 def test_why_use_a_function(self): 42 def add_x(number): 43 return 3 + number 44 45 for a_number in range(1000): 46 with self.subTest(a_number): 47 self.assertEqual( 48 add_x(a_number), a_number+3 49 ) 50 51 def test_positional_arguments(self):the test passes.
I change the name of the test from test_why_use_a_function to test_why_use_a_for_loop
I add a git commit message
test_positional_arguments with for loops
test_positional_arguments has assertions that check what happens when a function is called with positional arguments
self.assertEqual(
function(position_0, position_1),
(position_0, position_1)
)
I can use a for loop to remove repetition from the test.
RED: make it fail
I add a for loop for each one of the arguments used
51 def test_positional_arguments(self):
52 for first, second in (
53 (self.first, self.last),
54 (self.last, self.first),
55 (0, 1),
56 (self.a_tuple, self.a_list),
57 (self.a_set, self.a_dictionary),
58 ):
59 with self.subTest(inputs=(first, second)):
60 self.assertEqual(
61 src.functions.positional_arguments(
62 first, second
63 ),
64 (second, first)
65 )
66
67 positional_arguments = (
68 src.functions.positional_arguments
69 )
the terminal is my friend, and shows AssertionError
SUBFAILED(inputs=('first', 'last')) ...
AssertionError: Tuples differ:
('first', 'last') != ('last', 'first')
SUBFAILED(inputs=('last', 'first')) ...
AssertionError: Tuples differ:
('last', 'first') != ('first', 'last')
SUBFAILED(inputs=(0, 1)) ...
AssertionError: Tuples differ:
(0, 1) != (1, 0)
SUBFAILED(inputs=((0, 1, 2, 'n'), [0, 1, 2, 'n'])) ...
AssertionError: Tuples differ:
((0, 1, 2, 'n'), [0, 1, 2, 'n'])
!= ([0, 1, 2, 'n'], (0, 1, 2, 'n'))
SUBFAILED(inputs=({0, 1, 2, 'n'}, {'key': 'value'})) ...
AssertionError: Tuples differ:
({0, 1, 2, 'n'}, {'key': 'value'})
!= ({'key': 'value'}, {0, 1, 2, 'n'})
GREEN: make it pass
I change the expectation of the assertion in test_positional_arguments
REFACTOR: make it better
I add a for loop with calls to the positional_arguments, keyword_arguments, args_and_kwargs and optional_arguments functions
51 def test_positional_arguments(self): 52 for first, second in ( 53 (self.first, self.last), 54 (self.last, self.first), 55 (0, 1), 56 (self.a_tuple, self.a_list), 57 (self.a_set, self.a_dictionary), 58 ): 59 for a_function in ( 60 src.functions.positional_arguments, 61 src.functions.keyword_arguments, 62 src.functions.args_and_kwargs, 63 src.functions.optional_arguments, 64 ):65 with self.subTest( 66 a_function=a_function, inputs=(first, second) 67 ): 68 self.assertEqual( 69 a_function(first, second), 70 (second, first) 71 ) 72 73 positional_arguments = ( 74 src.functions.positional_arguments 75 )the terminal is my friend, and shows AssertionError for twenty sub tests.
I change the expectation of the assertion in test_positional_arguments
68 self.assertEqual( 69 a_function(first, second), 70 (first, second) 71 ) 72 73 positional_arguments = ( 74 src.functions.positional_arguments 75 )the test passes.
I remove the other statements from test_positional_arguments because they are now covered by the for loop
I add a git commit message
test_keyword_arguments with for loops
test_keyword_arguments has assertions that check what happens when a function is called with keyword arguments
self.assertEqual(
function(
last_input=something,
first_input=something_else),
(something_else, something)
)
I can use a for loop to remove repetition from the test.
RED: make it fail
I add a for loop for each one of the arguments and the positional_arguments, keyword_arguments and optional_arguments functions because they all have the same signature
73 def test_keyword_arguments(self):
74 for first, second in (
75 (self.first, self.last),
76 (self.last, self.first),
77 (0, 1),
78 (self.a_tuple, self.a_list),
79 (self.a_set, self.a_dictionary),
80 ):
81 for a_function in (
82 src.functions.positional_arguments,
83 src.functions.keyword_arguments,
84 src.functions.optional_arguments,
85 ):
86 with self.subTest(
87 a_function=a_function, inputs=(first, second)
88 ):
89 self.assertEqual(
90 a_function(
91 last_input=first,
92 first_input=second,
93 ),
94 (first, second)
95 )
96
97 keyword_arguments = (
98 src.functions.keyword_arguments
99 )
the terminal is my friend, and shows AssertionError for fifteen sub tests.
GREEN: make it pass
I change the expectation of the assertion in test_keyword_arguments
89 self.assertEqual(
90 a_function(
91 last_input=first,
92 first_input=second,
93 ),
94 (second, first)
95 )
96
97 keyword_arguments = (
98 src.functions.keyword_arguments
99 )
the test passes.
REFACTOR: make it better
I add the args_and_kwargs function to the for loop
73 def test_keyword_arguments(self): 74 for first, second in ( 75 (self.first, self.last), 76 (self.last, self.first), 77 (0, 1), 78 (self.a_tuple, self.a_list), 79 (self.a_set, self.a_dictionary), 80 ): 81 for a_function in ( 82 src.functions.positional_arguments, 83 src.functions.keyword_arguments, 84 src.functions.args_and_kwargs, 85 src.functions.optional_arguments, 86 ):the terminal is my friend, and shows TypeError
SUBFAILED( a_function=<function args_and_kwargs at 0xffff01ab2c34>, inputs=('first', 'last') ) ...TypeError: args_and_kwargs() got an unexpected keyword argument 'first_input'. Did you mean 'last_input'? SUBFAILED( a_function=<function args_and_kwargs at 0xffff01ab2c34>, inputs=('last', 'first') ) ...TypeError: args_and_kwargs() got an unexpected keyword argument 'first_input'. Did you mean 'last_input'? SUBFAILED( a_function=<function args_and_kwargs at 0xffff01ab2c34>, inputs=(0, 1) ) ...TypeError: args_and_kwargs() got an unexpected keyword argument 'first_input'. Did you mean 'last_input'? SUBFAILED( a_function=<function args_and_kwargs at 0xffff01ab2c34>, inputs=((0, 1, 2, 'n'), [0, 1, 2, 'n']) ) ...TypeError: args_and_kwargs() got an unexpected keyword argument 'first_input'. Did you mean 'last_input'? SUBFAILED( a_function=<function args_and_kwargs at 0xffff01ab2c34>, inputs=({0, 1, 2, 'n'}, {'key': 'value'}) ) ...TypeError: args_and_kwargs() got an unexpected keyword argument 'first_input'. Did you mean 'last_input'?I open
__init__.pyfrom the functions folder in thesrcfolderI change
argumenttofirst_inputin the definition and the return of the args_and_kwargs function, insrc/functions/__init__.py7def optional_arguments( 8 first_input, last_input='doe', 9 ): 10 return first_input, last_input 11 12 13def args_and_kwargs(first_input, last_input): 14 return first_input, last_input 15 16 17def keyword_arguments(first_input, last_input):the test passes.
I remove the other assertions from test_keyword_arguments in
tests/test_functions.pyI add a git commit message
test_args_and_kwargs with for loops
test_positional_arguments and test_keyword_arguments now look the same, one uses for loops
self.assertEqual(
function(
last_input=something,
first_input=something_else),
(something_else, something)
)
RED: make it fail
I add the for loops for each one of the arguments and the positional_arguments, keyword_arguments, args_and_kwargs and optional_arguments functions
98 def test_args_and_kwargs(self):
99 for first, second in (
100 (self.first, self.last),
101 (self.last, self.first),
102 (0, 1),
103 (self.a_tuple, self.a_list),
104 (self.a_set, self.a_dictionary),
105 ):
106 for a_function in (
107 src.functions.positional_arguments,
108 src.functions.keyword_arguments,
109 src.functions.args_and_kwargs,
110 src.functions.optional_arguments,
111 ):
112 with self.subTest(
113 a_function=a_function, inputs=(first, second)
114 ):
115 self.assertEqual(
116 a_function(first, second),
117 (second, first)
118 )
119 self.assertEqual(
120 a_function(
121 last_input=first,
122 first_input=second,
123 ),
124 (first, second)
125 )
126 self.assertEqual(
127 a_function(
128 first, last_input=second,
129 ),
130 (second, first)
131 )
132 self.assertEqual(
133 a_function(
134 second, last_input=first,
135 ),
136 (first, second)
137 )
138
139 self.assertEqual(
140 src.functions.args_and_kwargs(
141 self.first, last_input=self.last
142 ),
143 (self.first, self.last)
144 )
the terminal is my friend, and shows AssertionError for twenty sub tests.
GREEN: make it pass
I change the expectation of the assertion for positional arguments
112 with self.subTest( 113 a_function=a_function, inputs=(first, second) 114 ): 115 self.assertEqual( 116 a_function(first, second), 117 (first, second) 118 ) 119 self.assertEqual( 120 a_function( 121 last_input=first, 122 first_input=second, 123 ), 124 (first, second) 125 )the terminal shows AssertionError for twenty sub tests for the assertion for keyword arguments.
I change the expectation of the assertion for keyword arguments
119 self.assertEqual( 120 a_function( 121 last_input=first, 122 first_input=second, 123 ), 124 (second, first) 125 ) 126 self.assertEqual( 127 a_function( 128 first, last_input=second, 129 ), 130 (second, first) 131 )the terminal shows AssertionError for twenty sub tests for the first assertion for positional and keyword arguments.
I change the expectation of the first assertion for positional and keyword arguments
126 self.assertEqual( 127 a_function( 128 first, last_input=second, 129 ), 130 (first, second) 131 ) 132 self.assertEqual( 133 a_function( 134 second, last_input=first, 135 ), 136 (first, second) 137 )the terminal shows AssertionError for twenty sub tests for the second assertion for positional and keyword arguments.
I change the expectation of the second assertion for positional and keyword arguments
132 self.assertEqual( 133 a_function( 134 second, last_input=first, 135 ), 136 (second, first) 137 ) 138 139 self.assertEqual( 140 src.functions.args_and_kwargs( 141 self.first, last_input=self.last 142 ), 143 (self.first, self.last) 144 )the test passes.
REFACTOR: make it better
I remove the other assertion from test_args_and_kwargs
I remove test_positional_arguments and test_keyword_arguments because they are now covered by test_args_and_kwargs
I add a git commit message
close the project
I close
tests/test_functions.pyandsrc/functions/__init__.pyI click in the terminal where the tests are running
I use q on the keyboard to leave the tests
I change directory to the parent of
functions
review
I used for loops to remove repetition from the tests. If you did not go through this project and someone gave you this code, do you think you can tell what it is testing?
code from the chapter
what is next?
rate pumping python
If this has been a 7 star experience for you, please CLICK HERE to leave a 5 star review of pumping python. It helps other people get into the book too.