Main Content

Testing code in Python - doctest, unittest and others

Archive - Originally posted on "The Horse's Mouth" - 2009-09-16 19:41:00 - Graham Ellis

The doctest and unittest modules of Python allow you to provide test harnesses for your classes / packages. Designing applications from the bottom up, you'll want to ensure that each of your code levels works and works well. You'll want to provide an example of what it should do for the next level or programmer up, and you may want to be able to run the code time and time again - perhaps to test some device or algorithm you're plugging in underneath - and get a test report out from that.

With the doctest module, you initially run the python class from the interactive command line and the cut and paste the interaction into a documentation string. When you then run the code to test it, the doctest module checks that the test reproduces the same output that's in the docstring. There's an example here.

The unittest module requires you to write a series of test methods, each starting with the word "test" in their name and including code that asserts what conditions they should find after completion - perhaps an equality, or a certain value exists somewhere in a list. And at the end of all the tests, the module produces a report of which tests passed and failed. There are examples of unittest code here and here

Here's an example of unittest - I have added another test to the source code examples above (which test the system's random module!) and in this first case it all worked.

92:1 grahamellis$ python utter
testchoice (__main__.TestSequenceFunctions) ... ok
testflop (__main__.TestSequenceFunctions) ... ok
testsample (__main__.TestSequenceFunctions) ... ok
testshuffle (__main__.TestSequenceFunctions) ... ok
 
----------------------------------------------------------------------
Ran 4 tests in 0.007s
 
OK
92:1 grahamellis$


Now altering the code to assert that 42 is a number that can be found in a list of suffled numbers from 0 to 10 (which clearly it is not!) I got the following result:

92:1 grahamellis$ python utter
testchoice (__main__.TestSequenceFunctions) ... ok
testflop (__main__.TestSequenceFunctions) ... FAIL
testsample (__main__.TestSequenceFunctions) ... ok
testshuffle (__main__.TestSequenceFunctions) ... ok
 
======================================================================
FAIL: testflop (__main__.TestSequenceFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "utter", line 21, in testflop
    self.assert_(element in self.seq)
AssertionError
 
----------------------------------------------------------------------
Ran 4 tests in 0.100s
 
FAILED (failures=1)
92:1 grahamellis$


Python also has a profiler module that allows you to see your code coverage, and you should always remember to add a short test harness onto the end of your modules to ensure that the module, when loaded stand alone, is selfvalidating. There's an example here.