Using the doctest Module

Python has considerable support for testing, with the doctest and unittest modules for unit testing and the test module for regression testing. PyQt also provides unit-testing functionality with the QtTest module.

When we create modules, such as the length and ordereddict modules we wrote earlier, they are designed to be imported and the objects they provide (e.g., the Length and OrderedDict classes), used by the importing application. But since .py files can also be executables, we can easily include unit-testing code: When the module is imported the unit-testing code is simply ignored; but when the module is run the unit tests are executed. This approach is supported by the doctest module.

The doctest module makes unit testing as simple and painless as possible. To use it all we need to do is add examples to our docstrings, showing what we would type into the interactive Python interpreter (or IDLE) and what response we expect back. For example, here is the OrderedDict class's get() method in full:

def get(self, key, value=None):

Returns the value associated with key or value if key isn't in the dictionary

>>> d = OrderedDict(dict(s=1, a=2, n=3, i=4, t=5, y=6)) >>> d.get("X", 21) 21

ii II II

return self._dict.get(key, value)

The docstring contains a brief description of the method's purpose, and then some examples written as though they were typed into the interpreter. We begin by creating an OrderedDict object; we don't need to import or qualify since we are inside the ordereddict module. We then write a call to the method we are testing and what the interpreter (or IDLE) is expected to respond. And then we do another call and response.

The doctest module uses this syntax because it is so familiar to Python programmers through their use of the interactive Python interpreter or of IDLE, or of any other Python IDE, such as Eric4, that embeds a Python interpreter. When the tests are run, the doctest module will import the module itself, then read every docstring (using Python's introspection capabilities) and then execute each statement that begins with the >>> prompt. It then checks the result against the expected output (which may be nothing), and will report any failures.

To make a module able to use doctest like this we just need to add three lines at the end of the module:

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

Whether a module is imported by being the subject of an import statement, or is invoked on the command line, all the module's code is executed. This causes the module's functions and classes to be created ready for use.

We can tell whether a module was imported because in this case its __name__ attribute is set to the module's name. On the other hand, if a module is invoked its __name__ attribute is set to __main__ .

As shown earlier, we can use an if statement to see whether the module was imported, in which case we do nothing else. But if the module was invoked on the command line, we import the doctest module and execute the testmod() function which performs all our tests.

We can perform a test run from inside a console window. For example:

C:\>cd c:\pyqt\chap03 C:\pyqt\chap03>

If there are no test failures, the module will run silently. If there are any errors, these will be output to the console. We can force the doctest module to be more verbose by using the -v flag:

C:\pyqt\chap03> -v

This shows every single test that is performed, and a summary at the end.

It is also possible to test for expected failures, for example, out-cases where we expect an exception to be raised. For these we just write the first and last lines of the expected output (because the traceback in the middle may vary) and use an ellipsis, ..., to indicate the traceback. For example, here is the OrderedDict class's setAt() method in full:

def setAt(self, index, value):

Sets the index-th item's value to the given value

>>> d = OrderedDict(dict(s=1, a=2, n=3, i=4, t=5, y=6)) >>> d.getAt(5) 6

>>> d.setAt(5, 99) >>> d.getAt(5) 99

Traceback (most recent call last):

IndexError: list index out of range ii ii ii self._dict[self._keys[index]] = value

We created an OrderedDict of six items, but in the last test attempted to set the nonexistent twentieth item's value. This causes the dictionary to raise an IndexError, so we write what the interactive Python interpreter would output, and the doctest module understands this and will pass the test if the exception was correctly raised.

The doctest module is less sophisticated than the unittest module, but it is both easy to use and unobtrusive. We have used it in all the examples shown so far, as can be seen by looking at the book's source code.

Was this article helpful?

0 0
Tube Traffic Ninja

Tube Traffic Ninja

Discover How You Can Quickly And Easily Dominate Google and YouTube... With Simple Cash Generating Videos. Did you know that YouTube is the second largest search website on the entire Internet? YouTube gets more daily searches than Bing and Yahoo. In fact, there is only one search engine that gets more action.

Get My Free Ebook

Post a comment