functions: test_functions_w_keyword_arguments¶
There is an inherent problem with using positional arguments in functions. It requires the inputs to always be supplied in the right order. If the program is dependent on that order, then it will behave in an unintended way when it receives input out of order.
To ensure the function behaves right regardless of what order the user provides the input I can use Keyword Arguments
test_functions_w_keyword_arguments¶
red: make it fail¶
I add a new test to test_functions.py
def test_functions_w_keyword_arguments(self):
self.assertEqual(
functions.passthrough_w_keyword_arguments(
first_name='my_first_name',
last_name='my_last_name'
),
('my_first_name', 'my_last_name')
)
the terminal shows AttributeError
green: make it pass¶
I add a function definition to
functions.py
def passthrough_w_keyword_arguments(): return None
the terminal shows TypeError
TypeError: passthrough_w_keyword_arguments() got an unexpected keyword argument 'first_name'
I make the function signature to make it take a positional argument
def passthrough_w_keyword_arguments(first_name): return None
the terminal shows TypeError with a different message
TypeError: passthrough_w_keyword_arguments() got an unexpected keyword argument 'last_name'
I make the function signature to take in another positional argument
def passthrough_w_keyword_arguments(first_name, last_name): return None
the terminal shows AssertionError
I adjust the return statement to make the test pass
def passthrough_w_keyword_arguments(first_name, last_name): return first_name, last_name
Eureka! the terminal shows passing tests
refactor: make it better¶
So far
passthrough_w_keyword_arguments
looks the same aspassthrough_w_positional_arguments
did when it took in 2 positional arguments, I have not yet seen a difference between apositional argument
and akeyword argument
. I add an assertion that puts the input data out of order to see if there is a differencedef test_functions_w_keyword_arguments(self): self.assertEqual( functions.passthrough_w_keyword_arguments( first_name='my_first_name', last_name='my_last_name' ), ('my_first_name', 'my_last_name') ) self.assertEqual( functions.passthrough_w_keyword_arguments( last_name='my_last_name', first_name='my_first_name' ), ('my_first_name', 'my_last_name') )
the terminal shows passing tests. Unlike in
test_functions_w_positional_arguments
using the name when passing inputs, ensures the function always shows output in the right order regardless of the order in which the input data is givenThe function currently only takes in 2 keyword arguments. What if I want a function that can take in any number of keyword arguments? There is a starred expression for keyword arguments -
**
. I add an assertiondef test_functions_w_keyword_arguments(self): self.assertEqual( functions.passthrough_w_keyword_arguments( first_name='my_first_name', last_name='my_last_name' ), ('my_first_name', 'my_last_name') ) self.assertEqual( functions.passthrough_w_keyword_arguments( last_name='my_last_name', first_name='my_first_name' ), ('my_first_name', 'my_last_name') ) self.assertEqual( functions.passthrough_w_keyword_arguments( a=1, b=2, c=3, d=4 ), {} )
the terminal shows TypeError
I make the signature of
passthrough_w_keyword_arguments
take any number of keyword argumentsdef passthrough_w_keyword_arguments(**keyword_arguments): return keyword_arguments
the terminal shows AssertionError for the previous test that was passing. I have introduced a regression - the new code has caused an old passing test to fail.
so I change the expectation
def test_functions_w_keyword_arguments(self): self.assertEqual( functions.passthrough_w_keyword_arguments( first_name='my_first_name', last_name='my_last_name' ), { 'first_name': 'my_first_name', 'last_name': 'my_last_name' } )
the terminal shows AssertionError for the next test that was passing. I have another regression
I make the expectation match the output
def test_functions_w_keyword_arguments(self): self.assertEqual( functions.passthrough_w_keyword_arguments( first_name='my_first_name', last_name='my_last_name' ), { 'first_name': 'my_first_name', 'last_name': 'my_last_name' } ) self.assertEqual( functions.passthrough_w_keyword_arguments( last_name='my_last_name', first_name='my_first_name' ), { 'first_name': 'my_first_name', 'last_name': 'my_last_name' } )
and the terminal shows AssertionError for the last test I added
time to match the last test to the expected value in the comparison
def test_functions_w_keyword_arguments(self): self.assertEqual( functions.passthrough_w_keyword_arguments( first_name='my_first_name', last_name='my_last_name' ), { 'first_name': 'my_first_name', 'last_name': 'my_last_name' } ) self.assertEqual( functions.passthrough_w_keyword_arguments( last_name='my_last_name', first_name='my_first_name' ), { 'first_name': 'my_first_name', 'last_name': 'my_last_name' } ) self.assertEqual( functions.passthrough_w_keyword_arguments( a=1, b=2, c=3, d=4 ), {'a': 1, 'b': 2, 'c': 3, 'd': 4} )
the terminal shows passing tests. From these tests, I can say that keyword arguments are treated as dictionaries in Python
I add one more assertion to
test_functions_w_keyword_arguments
to drill the lessondef test_functions_w_keyword_arguments(self): self.assertEqual( functions.passthrough_w_keyword_arguments( first_name='my_first_name', last_name='my_last_name' ), { 'first_name': 'my_first_name', 'last_name': 'my_last_name' } ) self.assertEqual( functions.passthrough_w_keyword_arguments( last_name='my_last_name', first_name='my_first_name' ), { 'first_name': 'my_first_name', 'last_name': 'my_last_name' } ) self.assertEqual( functions.passthrough_w_keyword_arguments( a=1, b=2, c=3, d=4 ), {'a': 1, 'b': 2, 'c': 3, 'd': 4} ) self.assertEqual( functions.passthrough_w_keyword_arguments( a_boolean=bool, an_integer=int, a_float=float, a_string=str, a_tuple=tuple, a_list=list, a_set=set, a_dictionary=dict ), {} )
the terminal shows AssertionError and I make the expected values match the values from the terminal
self.assertEqual( functions.passthrough_w_keyword_arguments( a_boolean=bool, an_integer=int, a_float=float, a_string=str, a_tuple=tuple, a_list=list, a_set=set, a_dictionary=dict ), { 'a_boolean': bool, 'an_integer': int, 'a_float': float, 'a_string': str, 'a_tuple': tuple, 'a_list': list, 'a_set': set, 'a_dictionary': dict } )
All tests are passing!
review¶
From the tests I know
I can use
**name
to represent any number of keyword argumentsthat keyword arguments are represented as dictionaries with curly braces -
{}
I can use
*name
to represent any number of positional argumentsthat positional arguments are represented as tuples with parentheses -
()
that passthrough functions return what they receive as input
that singleton functions return the same thing every time they are called
Would you like to test functions with positional and keyword arguments?