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 as passthrough_w_positional_arguments did when it took in 2 positional arguments, I have not yet seen a difference between a positional argument and a keyword argument. I add an assertion that puts the input data out of order to see if there is a difference

    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')
        )
        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 given

  • The 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 assertion

    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')
        )
        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 arguments

    def 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 lesson

    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}
        )
        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 arguments

  • that keyword arguments are represented as dictionaries with curly braces - {}

  • I can use *name to represent any number of positional arguments

  • that 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

  • functions are defined using the def keyword

  • functions return None by default

Would you like to test functions with positional and keyword arguments?


functions: tests and solutions