The basic building blocks of unit testing aretest cases --single scenarios that must be set up and checked for correctness. Inunittest, test cases are represented by instances ofunittest'sTestCase class. To makeyour own test cases you must write subclasses ofTestCase, oruseFunctionTestCase.
An instance of aTestCase-derived class is an object that cancompletely run a single test method, together with optional set-upand tidy-up code.
The testing code of aTestCase instance should be entirelyself contained, such that it can be run either in isolation or inarbitrary combination with any number of other test cases.
The simplestTestCase subclass will simply override therunTest() method in order to perform specific testing code:
import unittestclass DefaultWidgetSizeTestCase(unittest.TestCase): def runTest(self): widget = Widget('The widget') self.assertEqual(widget.size(), (50, 50), 'incorrect default size')Note that in order to test something, we use the one of theassert*() orfail*() methods provided by theTestCase base class. If the test fails, an exception will beraised, andunittest will identify the test case as afailure. Any other exceptions will be treated aserrors.This helps you identify where the problem is:failures are caused byincorrect results - a 5 where you expected a 6.Errors are caused byincorrect code - e.g., aTypeError caused by an incorrectfunction call.
The way to run a test case will be described later. For now, notethat to construct an instance of such a test case, we call itsconstructor without arguments:
testCase = DefaultWidgetSizeTestCase()
Now, such test cases can be numerous, and their set-up can berepetitive. In the above case, constructing aWidget in each of100 Widget test case subclasses would mean unsightly duplication.
Luckily, we can factor out such set-up code by implementing a methodcalledsetUp(), which the testing framework willautomatically call for us when we run the test:
import unittestclass SimpleWidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget('The widget')class DefaultWidgetSizeTestCase(SimpleWidgetTestCase): def runTest(self): self.failUnless(self.widget.size() == (50,50), 'incorrect default size')class WidgetResizeTestCase(SimpleWidgetTestCase): def runTest(self): self.widget.resize(100,150) self.failUnless(self.widget.size() == (100,150), 'wrong size after resize')If thesetUp() method raises an exception while the test isrunning, the framework will consider the test to have suffered anerror, and therunTest() method will not be executed.
Similarly, we can provide atearDown() method that tidies upafter therunTest() method has been run:
import unittestclass SimpleWidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget('The widget') def tearDown(self): self.widget.dispose() self.widget = NoneIfsetUp() succeeded, thetearDown() method will berun whetherrunTest() succeeded or not.
Such a working environment for the testing code is called afixture.
Often, many small test cases will use the same fixture. In this case,we would end up subclassingSimpleWidgetTestCase into manysmall one-method classes such asDefaultWidgetSizeTestCase. This is time-consuming anddiscouraging, so in the same vein as JUnit,unittest providesa simpler mechanism:
import unittestclass WidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget('The widget') def tearDown(self): self.widget.dispose() self.widget = None def testDefaultSize(self): self.failUnless(self.widget.size() == (50,50), 'incorrect default size') def testResize(self): self.widget.resize(100,150) self.failUnless(self.widget.size() == (100,150), 'wrong size after resize')Here we have not provided arunTest() method, but haveinstead provided two different test methods. Class instances will noweach run one of thetest*() methods, withself.widgetcreated and destroyed separately for each instance. When creating aninstance we must specify the test method it is to run. We do this bypassing the method name in the constructor:
defaultSizeTestCase = WidgetTestCase('testDefaultSize')resizeTestCase = WidgetTestCase('testResize')Test case instances are grouped together according to the featuresthey test.unittest provides a mechanism for this: thetest suite, represented byunittest'sTestSuiteclass:
widgetTestSuite = unittest.TestSuite()widgetTestSuite.addTest(WidgetTestCase('testDefaultSize'))widgetTestSuite.addTest(WidgetTestCase('testResize'))For the ease of running tests, as we will see later, it is a goodidea to provide in each test module a callable object that returns apre-built test suite:
def suite(): suite = unittest.TestSuite() suite.addTest(WidgetTestCase('testDefaultSize')) suite.addTest(WidgetTestCase('testResize')) return suiteor even:
def suite(): tests = ['testDefaultSize', 'testResize'] return unittest.TestSuite(map(WidgetTestCase, tests))
Since it is a common pattern to create aTestCase subclasswith many similarly named test functions,unittest provides aTestLoader class that can be used to automate the process ofcreating a test suite and populating it with individual tests.For example,
suite = unittest.TestLoader().loadTestsFromTestCase(WidgetTestCase)
will create a test suite that will runWidgetTestCase.testDefaultSize() andWidgetTestCase.testResize.TestLoader uses the'test' method name prefix to identifytest methods automatically.
Note that the order in which the various test cases will be run isdetermined by sorting the test function names with the built-incmp() function.
Often it is desirable to group suites of test cases together, so as torun tests for the whole system at once. This is easy, sinceTestSuite instances can be added to aTestSuite justasTestCase instances can be added to aTestSuite:
suite1 = module1.TheTestSuite()suite2 = module2.TheTestSuite()alltests = unittest.TestSuite([suite1, suite2])
You can place the definitions of test cases and test suites in thesame modules as the code they are to test (such aswidget.py),but there are several advantages to placing the test code in aseparate module, such astest_widget.py: