Posts Tagged ‘doctest’

Testing in Python Using doctest

August 26, 2007

Along with using version control, another absolute key to developing reliable software is to systematically test your code as you write it. After all, source code needs to be bug-free to function properly, but all human beings generate bugs at a very high rate when writing code.

Fortunately, Python makes testing remarkably easy and convenient with its doctest module, which lets you put your tests right into your doc strings.

Here’s a tiny example of its use (for more, go to the documentation):


def cube(x):
    """
    >>> cube(10)
    1000
    """
    return x * x

def _test():
    import doctest
    doctest.testmod()

if __name__ == "__main__":
    _test()

I intentionally left a bug in this code. Here’s what happens when I run it:


[yedidia ~] python cube.py
**********************************************************************
File "cube.py", line 3, in __main__.cube
Failed example:
    cube(10)
Expected:
    1000
Got:
    100
**********************************************************************
1 items had failures:
   1 of   1 in __main__.cube
***Test Failed*** 1 failures.

An advantage of using doctest is that your doc strings will serve as examples, as well as tests, of the functions that they document. Examples are often the best kind of documentation for a function.

In fact, I find that if a function doc string explains the inputs to the function, the variable(s) returned by the function, and any side effects, along with the doctest examples, then there is rarely any need for other comments.

My favorite way to develop python code is actually within Emacs. I write a test for a function, then write the function itself, and then type Control-C Control-C in Emacs. Control-C Control-C will execute the python code. If your code is set up to run a _test() function like the code above, then Emacs will open up another buffer which will contain any doctest failures. When all the tests pass, I finish up the documentation of the inputs, outputs, and side effects. That way you can systematically build up your software, one reliable and documented function at a time, while never leaving Emacs.

Emin Martinian taught me this technique. Another approach is to use the IPython enhanced python shell.

Note: to have Control-C Control-C execute python code, you’ll need to add the following lines to your .emacs file (more details here):


(autoload 'python-mode "python-mode" "Python Mode." t)
(add-to-list 'auto-mode-alist '("\.py\'" . python-mode))
(add-to-list 'interpreter-mode-alist '("python" . python-mode))

Of course, you should always keep all your old tests, both because they serve as examples, and also because if any new code somehow breaks an old function, you’ll see it immediately.

Advertisements