Testing

Take a simple function

def factorial(n):
    total = 1
    while n > 1: 
        total = total * n
        n = n - 1
    return total

Test method 1: call the function

Just call the function a few times with different values and observe the output:

print factorial(0)
print factorial(1)
print factorial(2)
print factorial(3)

Expect the output:

1
1
2
6

Test method 2: simple boolean tests

Create expressions that call the function and each time compare it to the expected output:

print factorial(0) == 1
print factorial(1) == 1
print factorial(2) == 2
print factorial(3) == 6

Expect:

True
True
True
True

If any of these tests return False, you will not know the value that they return and you will need to investigate further, but this introduces the idea of a test that either passes, returning True, or fails, returning False. Unlike the first test method, these are self-contained tests that tell you at a glance whether or not the function is returning the correct value.

Interlude: Assertions

Assertions are commonly used inside functions to test for errors, like this:

def factorial(n):
    assert n >= 0
    total = 1
    while n > 1: 
        total = total * n
        n = n - 1
    return total

In this case, the program will terminate if you attempt to evaluate the factorial of a negative number.

Assertions are a useful addition to the process of testing your code, but are not a replacement for creating separate tests outside your functions.

Test method 3: The “Udacity” method

def test():
    test_cases = [(0,1),
                  (1,1),
                  (2,2),
                  (3,6)]
    for (arg, answer) in test_cases:
        result = factorial(arg)
        if result != answer:
            print "Test with data:", arg, "failed with result ", result
        else:
            print "Test case passed!"

test()

This has the advantages over simple one-line assertions, of allowing you to easily add new test cases and of allowing you to print more information when a test fails.

Test method 4: unittest

unitest is the standard Python unit testing framework.

import unittest

class FactorialTest(unittest.TestCase):
     def test(self):
        self.assertEqual(factorial(0), 1)
        self.assertEqual(factorial(1), 1)
        self.assertEqual(factorial(2), 2)
        self.assertEqual(factorial(3), 6)

if __name__ == '__main__':
    unittest.main()

You can return to this after we have looked at object-oriented programming, when class and self will make more sense.

Test method 5: doctest

def factorial(n):
    """Return the factorial of n, an exact integer >= 0

    >>> [factorial(n) for n in range(6)]
    [1, 1, 2, 6, 24, 120]
    """
    total = 1
    while n > 1:
        total = total * n
        n = n - 1
    return total

if __name__ == "__main__":
    import doctest
    doctest.testmod()

You can write human-readable comments in your code, which with the magic of >>>, will execute the code in your comments and check that the output is the same as following line.

Other methods

This quick summary does not even begin to exhaust the possible ways of testing in Python. As your applications gets more complex, other testing libraries are available to help you manage that complexity. If you are curious, take a look at pytest, nose, tox, unittest2 and mock.