Testing your Python classes with the unittest package - how to
Archive - Originally posted on "The Horse's Mouth" - 2011-10-14 06:20:05 - Graham EllisPython's unittest module - supplied within the base distribution - allows you to define and run a series of tests for a class or group of classes, and correlate the results, reporting any transgressions in a standard format without having the whole test sequence break.
Testing your code and obtaining correct results is vital whatever your application, and unit tests allow you define a series of tests, state what the answers should be, and re-run those tests time and again. Making a small alteration to the code? Rerun the unittests so that you know you haven't accidentally introduced an error!
We have a short "hello testing world" example that we use on on ALL of our Python courses (testing your code to a level that you choose is important - this is not an example I will leave out!). Full source [here]. How does it work?
1. You load your class for testing.
2. You load unittest and extend unittest.TestCase into your own TestSequenceFunctions class.
3. You define a method called setUp within the test class which initialises any data that you may need for use during the tests
4. You define as few (or as many) methods as you like with name starting test... which make calls to the class your testing then use test class methods to check that the results returned are as expected.
5. You call unittest.main to run the tests.
Here are the results of running the tests in the "hello testing world" example, showing a correct set of results:
wizard:oct11 graham$ python utes.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
wizard:oct11 graham$
And really THAT is how simple you want it. A quick running of the tests and it's saying "yes, they all worked as you predicted".
What happens if something goes wrong? I have introduced a small bug into the program so that one of the tests fails, and my results from running the test suite are very different:
wizard:oct11 graham$ python utes.py
..F
======================================================================
FAIL: testshuffle (__main__.TestSequenceFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
File "utes.py", line 13, in testshuffle
self.assertEqual(self.seq, range(10))
AssertionError: Lists differ: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9,... != [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
First list contains 1 additional elements.
First extra element 10:
10
- [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
? ----
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
----------------------------------------------------------------------
Ran 3 tests in 0.002s
FAILED (failures=1)
wizard:oct11 graham$
Plenty of information there - and all because I passed an 11 element list into a test where I asserted it would be just 10 elements long. The error message shows me where the error is in general terms - "these lists are not the same" and then goes on to tell be how - "look - this list is longer because there's an extra element 10" and even "Look - it's HERE!".
The three tests used in this example are illustrative - there are a number of test methods available in the unittest.Testcase class:
assertEqual to check if two objects are equal after test code has been run
assert_ or assertTrue to check if something is true after test code has been run
assertRaises to check if a particular expection is raise by test code
and you can probably see from their names what the following do:
assertNotEqual, assertFalse, assertAlmostEqual, assertAlmostEqual
The above example is heavily based on the one in the standard Python documentation [link] ... and we've also got an extended alternative on our website [here] which gives a more vervose report:
wizard:oct11 graham$ python utes2
testchoice (__main__.TestSequenceFunctions) ... ok
testsample (__main__.TestSequenceFunctions) ... ok
testshuffle (__main__.TestSequenceFunctions) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
wizard:oct11 graham$
On the private Python course I was running earlier this week, we spent more time than usual on testing - and I added a test harness into a class which I had written to illustrate oveloaded operators - adding and multiplying "mean time between failure" objects.
As is usual with modules that I write, I included a short piece of code which is to be run only of the class is run on its own - starting with
if __name__ == "__main__":
Actually within that conditional code, I imported the unittest module and defined and ran my test sequence - dynamic and conditional loading is great as it means that the test cases and unittest module will NOT be loaded if the module is loaded as part of a full application.
Also within the test code I have printed out the __doc__ documentation string for the module so that testing automatically provides a brief description of the module / class being tested. See [here] for the full source code of the new demo, onto which I have added a full set of sample output.
As of Python 2.7, a considerable number of extra test case assertion methods have been added, allowing (for example) for testing against regular expressions. There's a new capability of running tests from the command line too ... the various additions showing how important the team at the core of Python consider testing to be. I'm mentioning these new methods on our courses, but concentrating on the well estanblished methods at present since - at the time of writing - many of you are using modules / code / systems which still require / use an earlier version of Python such as 2.6.