Testing

Unit Testing with Python

By José R.C. Cruz, March 17, 2014

Python has substantial resources to enable unit testing

Unit testing is considered an essential part of software development. Through unit testing, we can evaluate each code component, find out how well it performs, and determine how well it reacts to valid or invalid input. A regression suite of unit tests is also an excellent way of detecting unexpected changes in a code base caused by refactoring or writing new code.

In this article, I examine the mechanisms of unit testing in Python, starting with the unittest module and its key classes. I examine tests individually and in suites, and I discuss how to facilitate their construction and use. Readers should have a working knowledge of Python. The sample test code requires Python 2.5 or later.

The unittest Module

The unittest module started life as the third-party module PyUnit. PyUnit was a Python port of JUnit, the Java unit testing framework. Designed by Steve Purcell, PyUnit became an official Python module starting with version 2.5.

Figure 1: Core classes in unittest.

As Figure 1 shows, there are five key classes in the unittest module. The TestCase class holds the test routines and provides hooks for preparing each routine and for cleaning up after. The TestSuite class serves as a collection container. It can hold multiple TestCase objects and multiple TestSuite objects.

The TestLoader class loads test cases and suites defined locally or from an external file. It emits a TestSuite object that holds those cases and suites. The TextTestRunner class provides a standard platform to run the tests. The TestResults class provides a standard container for the test results.

Out of these five classes, only TestCase must be subclassed. The other four classes can also be subclassed, but they are generally used as is.

Preparing a Test Case

Figure 2 shows the structure of the TestCase class. In it are three sets of methods that are used most often in designing the tests. In the first set are the pre- and post-test hooks. The setUp() method fires before each test routine, the tearDown() after the routine. Override these methods when you create a custom test case.

Figure 2: The structure of a TestCase class.

The second pair of methods control test execution. Both methods take a message string as input, and both abort an ongoing test. But the skipTest() method cancels the current test, while the fail() method fails it explicitly.

The third set of methods help identify the test. The method id() returns a string containing the name of the TestCase object and of the test routine. And the method shortDescription() returns the docstr comment at the start of each test routine. If the routine has no such comment, shortDescription() returns a None.

Listing One shows the sample bare bones test case FooTest. FooTest has two test routines: testA() and testB(). Both routines get the required argument of self. Both have a docstr comment for a first line.

Note the same setUp() and tearDown() methods run before and after each test routine. So how do you let setUp() and tearDown() know which routine is being run? You must first identify the routine by calling shortDescription() or id() (See Listing Two). Then use an if-else block to route to the appropriate code. In the sample snippet, FooTest calls shortDescription() to get the routine's docstr comment, then runs the prep and clean-up code for that routine.

Designing a Test Routine

Each test routine must have the prefix "test" in its name. Without that prefix, the routine will not run. To perform a test, the test routine should use an assert method. An assert method gets one or more test arguments and an optional assert message. When a test fails, the assert halts the routine and sends the error message to stdout.

There are three sets of assert methods. In the first set (Table 1) are the basic Boolean asserts, which fire on a True or False result.

Assert

Complement Assert

Operation

assertTrue(a, M)

assertFalse(a, M)

a = True; a = False

assertEqual(a, b, M)

assertNotEqual(a, b, M)

a = b; a ≠ b

assertIs(a, b, M)

assertIsNot(a, b, M)

a is b; a is not b

assertIsNone(a, M)

assertIsNotNone(a, M)

a = nil; a ≠ nil

AssertIsInstance(a, b, M)

AssertIsNotInstance(a, b, M)

isinstance(a,b);not isinstance(a,b)

Table 1: Basic asserts in unittest.

To check for just a True or False, use assertTrue() or assertFalse(), as in Listing Three:

Listing Three: Checking for True or False.

self.assertTrue(argState, "foobar() gave back a False")
# -- fires when the instance method foobar() returns a True
self.assertFalse(argState)
# -- fires when foobar() returns a False
# Notice this one does not supply an assert message

To check whether two arguments are the same, use assertEqual() and assertNotEqual() as in Listing Four. These last two asserts check the arguments' values, as well as their data types.

To check if the arguments are the same objects, use assertIs() and assertIsNot(). Like assertEqual() and assertNotEqual(), these two asserts examine both argument values and type. To check if an argument is an instance of a specific class, use assertIsInstance() and assertIsNotInstance() as in Listing Five.

Listing Five: Checking if an argument is an instance of a specific class.

argFoo = Bar()
# checking against class Bar
self.assertIsInstance(argFoo, Bar, "The object is not an instance of class Bar")
# -- this assert will succeed
# checking against class Foo
self.assertIsNotInstance(argFoo, Foo, "The object is an instance of class Foo")
# -- this assert will fail

Both asserts get a class name as a second argument. Both behave similarly to the library function isInstance(). Finally, to check for a nil, use assertIsNone() and assertIsNotNone().

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!