Python'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.
(written 2011-10-14)
Associated topics are indexed as below, or enter http://melksh.am/nnnn for individual articles
Y212 - Python - Code testing, patterns, profiles and optimisation. [235] Preparation for a day's work - (2005-03-04)
[1140] Python GTK - Widget, Packing, Event and Feedback example - (2007-04-09)
[1146] __new__ v __init__ - python constructor alternatives? - (2007-04-14)
[1148] Python decorators - wrapping a method call in extra code - (2007-04-15)
[1555] Advanced Python, Perl, PHP and Tcl training courses / classes - (2008-02-25)
[2123] Using Python with OpenOffice - (2009-04-09)
[2616] Defining a static method - Java, Python and Ruby - (2010-02-01)
[3441] Pressing ^C in a Python program. Also Progress Bar. - (2011-09-15)
[3442] A demonstration of how many Python facilities work together - (2011-09-16)
[3464] Passing optional and named parameters to python methods - (2011-10-04)
[3658] Using Make for a distribution - (2012-03-17)
[4090] Test Driven Development in Python - Customer Comes First - (2013-05-16)
[4326] Learning to program - comments, documentation and test code - (2014-11-22)
[4344] Python base and inherited classes, test harness and unit testing - new examples - (2014-12-07)
[4446] Combining tests into suites, and suites into bigger suites - Python and unittest - (2015-03-01)
[4470] Testing in Python 3 - unittest, doctest and __name__ == __main__ too. - (2015-04-21)
[4538] Flask and unittest - hello web app test world - (2015-10-15)
[4540] Unittest of a Flask application including forms - (2015-10-15)
[4542] The principle of mocking - and the Python Mock package - (2015-10-17)
[4617] Pytest - starting example - (2016-01-07)
[4618] Pytest - second example beyond hello world - (2016-01-08)
[4716] Profiling your Python program - (2016-11-01)
Some other Articles
Taking a boat down Caen Hill LocksSome thoughts in answer to some Melksham Campus questionsDirect Message: Really horrible blog about you ... a clever phishing trip, said to be from an MPPractical Extraction and Reporting - using Python and Extreme ProgrammingTesting your Python classes with the unittest package - how toChoosing your Python GUI - wx, Qt, Tk or GTK?Tkinter - an easy to use Python Graphic User Interface - introductory examplesHavant - Shop Frontages.Python Packages - groupings of modules. An introductionPicture - Havant Station at Dusk