test_functions


test_functions_w_pass

red: make it fail

  • I open a terminal to run makePythonTdd.sh with functions as the name of the project

    ./makePythonTdd.sh functions
    

    on Windows without Windows Subsystem Linux use makePythonTdd.ps1

    ./makePythonTdd.ps1 functions
    

    it makes the folders and files that are needed, installs packages, runs the first test, and the terminal shows AssertionError

    E       AssertionError: True is not false
    
    tests/test_functions.py:7: AssertionError
    
  • I hold ctrl (windows/linux) or option (mac) on the keyboard and use the mouse to click on tests/test_functions.py:7 to open it in the editor

  • then change True to False

  • and change test_failure

import unittest
import functions


class TestFunctions(unittest.TestCase):

    def test_functions_w_pass(self):
        self.assertIsNone(functions.function_w_pass())

the terminal shows ModuleNotFoundError , and I add it to the list of Exceptions encountered

# Exceptions Encountered
# AssertionError
# ModuleNotFoundError

green: make it pass

  • I make a file called functions.py in the project folder and the terminal shows AttributeError, which I add to the list of Exceptions encountered

    # Exceptions Encountered
    # AssertionError
    # ModuleNotFoundError
    # AttributeError
    
  • I add a function definition to functions.py

    def function_w_pass():
        pass
    

    and we have a passing test

    • the test checks if the value of the call to functions.function_w_pass is None

    • the function definition simply says pass yet the test passes

    • pass is a placeholder keyword which allows the function definition to follow Python syntax rules

    • the test passes because in Python all functions return None by default, like the function has an invisible line that says return None


test_functions_w_return

red: make it fail

I add a new failing test to TestFunctions in test_functions.py to check that functions always return None

def test_functions_w_return(self):
    self.assertIsNone(functions.function_w_return())

the terminal shows AttributeError

green: make it fail

I add a new function to functions.py to make the test pass, this time with a return statement instead of pass

def function_w_return(self):
    return

the terminal shows this test also passes

I defined 2 functions with different statements in their body but they both return the same result, because “in Python all functions return None by default, like the function has an invisible line that says return None

test_functions_w_return_none

red: make it fail

I add one more test to the TestFunctions class in test_functions.py to help drive home the point

def test_functions_w_return_none(self):
    self.assertIsNone(
        functions.function_w_return_none()
    )

the terminal shows AttributeError

green: make it pass

from the Zen of Python: Explicit is better than implicit. I add a function definition to functions.py this time with an explicit return statement showing the value returned

def function_w_return_none():
    return None

and the terminal shows passing tests.

test_singleton_functions

Singleton functions always return the same thing when called

red: make it fail

I add a test to test_functions.py

def test_singleton_functions(self):
    self.assertEqual(functions.singleton(), 'my_first_name')

the terminal shows AttributeError

green: make it pass

I change the function to make it pass

def singleton():
    return 'my_first_name'

test_singleton_functions_w_inputs

red: make it fail

I add a new test that checks if a singleton that takes inputs returns the same value regardless of the inputs

def test_singleton_functions_w_inputs(self):
    self.assertEqual(
        functions.singleton_w_inputs('Bob', 'James', 'Frank'),
        'joe'
    )
    self.assertEqual(
        functions.singleton_w_inputs('a', 2, 'c', 3),
        'joe'
    )

the terminal shows AttributeError

green: make it pass

and I add a function for singleton_w_inputs to functions.py

def singleton_w_inputs(*args):
    return 'joe'

the terminal shows passing tests


test_passthrough_functions

Passthrough functions return their input as output

red: make it fail

I add a failing test to the TestFunctions class in test_functions.py

def test_passthrough_functions(self):
    self.assertEqual(functions.passthrough(False), False)

the terminal shows AttributeError

green: make it pass

  • I add a function definition to functions.py

    def passthrough():
        return None
    

    the terminal shows TypeError

    TypeError: passthrough() takes 0 positional arguments but 1 was given
    

    because the definition for passthrough does not allow inputs and the test sends False as input

  • I add the error to the list of Exceptions encountered

    # Exceptions Encountered
    # AssertionError
    # ModuleNotFoundError
    # AttributeError
    # TypeError
    
  • then I make passthrough in functions.py to take 1 positional argument

    def passthrough(argument):
        return None
    

    and the terminal shows AssertionError

    AssertionError: None != False
    

    because the result of calling functions.passthrough with False as input is None which is not equal to the expected result (False)

  • I change the definition of passthrough to make the test pass

    def passthrough(argument):
        return False
    

    the terminal shows passing tests. I am genius!

refactor: make it better

Wait a minute! Something is not quite right here. The definition for a passthrough function was that it returned the same thing it was given, the test passes when False is given as input, will it still pass when another value is given or will it always return False? Time to write a test

  • I add a new assertion to test_passthrough_functions

    def test_passthrough_functions(self):
        self.assertEqual(functions.passthrough(False), False)
        self.assertEqual(functions.passthrough(True), True)
    

    the terminal shows AssertionError

    AssertionError: False != True
    

    the function returns False instead of True in the second case, I am not all the way genius, yet

  • I change the definition of passthrough in functions.py

    def passthrough(argument):
        return argument
    

    the terminal shows passing tests. I have more confidence that the passthrough function will return its input.

  • I add more tests for good measure using the other Python data structures

    def test_passthrough_functions(self):
        self.assertEqual(functions.passthrough(False), False)
        self.assertEqual(functions.passthrough(True), True)
        self.assertEqual(functions.passthrough(None), False)
        self.assertEqual(functions.passthrough(int), False)
        self.assertEqual(functions.passthrough(str), False)
        self.assertEqual(functions.passthrough(tuple), False)
        self.assertEqual(functions.passthrough(list), False)
        self.assertEqual(functions.passthrough(set), False)
        self.assertEqual(functions.passthrough(dict), False)
    

    the terminal shows AssertionError for each line until I make the input match the output, proving that the passthrough function I have defined returns the input it is given. Hooray! I am genius again


review

The 3 ways I have defined functions so far have the exact same outcome, they all return None. If Explicit is better than implicit. then I prefer to use return None telling anyone who reads the code exactly what the function returns.

Here is what I know so far from the tests

Would you like to test singleton functions?


functions: tests and solutions