Unittest
Introduction
The Python unit testing framework, sometimes referred to as PyUnit, is a Python language version of JUnit, by Kent Beck and Erich Gamma. JUnit is, in turn, a Java version of Kent's Smalltalk testing framework. Each is the de facto standard unit testing framework for its respective language (see docs)
Hence the right place to get started is Kent Beck's original paper. The basic pattern introduced in this paper and shared by unittest
is the following:
- test fixture:
A test fixture represents the preparation needed to perform one or more tests, and any associate cleanup actions. This may involve, for example, creating temporary or proxy databases, directories, or starting a server process. *
- test case:
A test case is the smallest unit of testing. It checks for a specific response to a particular set of inputs. unittest provides a base class, TestCase, which may be used to create new test cases. *
- test suite:
A test suite is a collection of test cases, test suites, or both. It is used to aggregate tests that should be executed together. *
- test runner:
A test runner is a component which orchestrates the execution of tests and provides the outcome to the user. The runner may use a graphical interface, a textual interface, or return a special value to indicate the results of executing the tests. *
Example
The examples below should give an idea on how the unittest
module can be used. They are not meant as good test examples, but rather as patterns to quickly get started writing own test case classes. For good unittest
examples simply look at the python libraries at: <path to libraries>/test/test_*.py
. To find the path to python, type import sys; print sys.path
in the python shell. To find test files on *nix systems, type find /usr/lib/python2.7/ -name *test_*.py
in a terminal.
Example 1: test case
""" Test case for fraction file: test_fraction.py """ import unittest from myfraction import fraction class TestFrac(unittest.TestCase): def setUp(self): self.zero = fraction(0, 1) self.one = fraction(1, 1) def test_return(self): f = fraction(2, 3) self.assertTrue(type(f) is tuple) def test_normalized_gcd(self): for n in range(1, 100): f = fraction(n, n) self.assertEqual(f, self.one) def test_normalized_sign(self): f = fraction(-1, -1) self.assertEqual(f, self.one) def test_zerodivision(self): with self.assertRaises(ZeroDivisionError): fraction(1, 0) if __name__ == "__main__": unittest.main()
The code above is a simple test case for the fraction function below. A test case is a subclass of unittest.TestCase
:
import unittest [...] class TestFrac(unittest.TestCase): [...]
All methods of this class, whose names start with test
are recognized as tests: test_return
, test_normalized_gcd
, test_normalized_sign
and test_zerodivision
. The actual tests are calls of assert functions. In the example above self.assertTrue(...)
checks whether the argument is True
, self.assertEqual(..., ...)
checks whether the two arguments are equal and:
with self.assertRaises(<exception>): <some code>
checks whether the given exception gets raised when executing some code. The full list can be found here.
The method setUp
, is executed before each call of a test method. It is meant to set up a clean test environment (test fixture) before each call of a test method. Similarly there is a tearDown
method, called after each test. Both methods have default implementations doing nothing.
""" Implementation of gcd and fraction, to demonstrate the use of the unittest module. file: myfraction.py """ def gcd(a,b): """ Returns the gcd of a and b """ while 0 != b: (a, b) = (b, a%b) return a def fraction(num, den): """ Returns a fraction as tuple """ if 0 == den: raise ZeroDivisionError('fraction(%s, 0)' % num) else: g = gcd(num, den) return (num//g, den//g)
The test case above can be executed by just executing python test_fraction.py
, thanks to the lines:
if __name__ == "__main__": unittest.main()
An alternative is the command line interface:
python -m unittest -v test_fraction.TestFrac
Example 2: test suite
""" Test suite for myfraction file: test_myfraction.py """ import unittest from test_gcd import TestGCD from test_fraction import TestFrac # Create a test suite for each test case # GCD suite suite_gcd = unittest.TestSuite() # only add one test for gcd suite_gcd.addTest(TestGCD('test_prime_products')) # Fraction suite # add all tests for fraction suite_frac = unittest.TestLoader().loadTestsFromTestCase(TestFrac) # Create a suite containing the two suits above suite_myfraction = unittest.TestSuite([suite_gcd, suite_frac]) # Instantiate the test runner runner = unittest.TextTestRunner() # Run the runner runner.run(suite_myfraction)
Test suites allow to organize the test code by aggregating test cases (and test suites) in test suites. The example above instantiates tree suites: suite_gcd
, suite_frac
and suite_myfraction
. suite_gcd
is first instantiated with uinittest.TestSuite()
, and then a single test method is added with addTest(...)
. For suite_frac
a test loader is used to load all test methods of the TestFrac test case:
suite_frac = unittest.TestLoader().loadTestsFromTestCase(TestFrac)
The two suites are then aggregated in one suite:
suite_myfraction = unittest.TestSuite([suite_gcd, suite_frac])
Finally a test runner is instantiated and then used to run the suite_myfraction
.
""" Test case for gcd file: test_gcd.py """ import unittest from myfraction import gcd class TestGCD(unittest.TestCase): def test_prime_products(self): a = gcd(2*5*11*17*23*31*41*47*59,\ 3*7*13*17*19*29*37*43*53) self.assertEqual(a, 17) def test_type(self): self.assertTrue(type(gcd(1,1)) is int) if __name__ == "__main__": unittest.main()