Overview• Unit TestingBasics: What, Why, When• How: Python Unit Test Class• Unit Test Writing Tips and Resources
3.
Unit Testing Basics•Functional tests: done by QA to testfunctionality according to a test plan basedon requirements and design specs.• Unit tests: done by developers to testspecific code. Typically “white box” testing.• Essential part of Extreme Programming andother agile methods.
4.
Why Write UnitTests• Increase developers’ confidence in code. Ifsomeone challenges your work, you can say“the tests passed.”• Avoid regression. If unit test suite is runfrequently, you know when new codebreaks old code.• If you write tests first, you know whenyou’re done, i.e., when the tests pass.• Encourages minimal interfaces andmodularity.
5.
When to Write/RunUnitTests• Always!• Before you check code into repository, soyou know your code works.• Before debugging, to ease the process andhelp you know when you’re done.
6.
Test Writing Tips•Make code modular: use interfaces/template classes/abstract baseclasses.• Use mock objects to inspect behavior ofobject you’re testing and to stand in for“heavy” objects, e.g., you don’t want to donetwork I/O in a unit test.• Modular, loosely coupled interfaces makemock objects possible.• Excessive coupling is enemy of unit testing.
7.
Using PyUnit towrite your own tests• InstallationThe classes needed to write tests are to befound in the 'unittest' module. This module ispart of the standard Python library for Python2.1 and later. If you are using an older Pythonversion, you should obtain the module fromthe separate PyUnit distribution.
8.
An introduction toTestCases• The basic building blocks of unit testing are'test cases' -- single scenarios that must be setup and checked for correctness. In PyUnit, testcases are represented by the TestCase class inthe unittest module. To make your own testcases you must write subclasses of TestCase.• An instance of a TestCase class is an object thatcan completely run a single test method,together with optional set-up and tidy-up code.
9.
Creating a simpletest case• The simplest test case subclass will simply overridethe runTest method in order to perform specific testing code:import unittestclass DefaultWidgetSizeTestCase(unittest.TestCase):def runTest(self):widget = Widget("The widget")assert widget.size() == (50,50), 'incorrect defaultsize'
10.
Note that inorder to test something, we just use thebuilt-in 'assert' statement of Python. If the assertionfails when the test case runs, an AssertionError willbe raised, and the testing framework will identify thetest case as a 'failure'. Other exceptions that do notarise from explicit 'assert' checks are identified bythe testing framework as 'errors'.
11.
Re-using set-up code:creating 'fixtures'• Now, such test cases can be numerous, and theirset-up can be repetitive. In the above case,constructing a 'Widget' in each of 100 Widget testcase subclasses would mean unsightly duplication.• Luckily, we can factor out such set-up code byimplementing a hook method called setUp, whichthe testing framework will automatically call for uswhen we run the test:
• If thesetUp method raises an exception while the test is running, theframework will consider the test to have suffered an error, andthe runTest method will not be executed.• Similarly, we can provide a tearDown method that tidies up afterthe runTest method has been run:import unittest classSimpleWidgetTestCase(unittest.TestCase):def setUp(self):self.widget = Widget("The widget")def tearDown(self):self.widget.dispose()self.widget = None• If setUp succeeded, the tearDown method will be run regardless of whetheror not runTest succeeded.• Such a working environment for the testing code is termed a fixture.
14.
TestCase classes withseveral test methods• Often, many small test cases will use the same fixture. In this case, we would end upsubclassing SimpleWidgetTestCase into many small one-method classes suchas DefaultWidgetSizeTestCase. This is time-consuming and discouragingimport unittest classWidgetTestCase(unittest.TestCase):def setUp(self):self.widget = Widget("The widget")def tearDown(self):self.widget.dispose()self.widget = Nonedef testDefaultSize(self):assert self.widget.size() == (50,50), 'incorrect default size'def testResize(self):self.widget.resize(100,150)assert self.widget.size() == (100,150), 'wrong size after resize'
15.
TestCase classes withseveral test methods• Here we have not provided a runTest method, but have instead providedtwo different test methods. Class instances will now each run one ofthe test methods, with self.widget created and destroyed separately foreach instance. When creating an instance we must specify the testmethod it is to run. We do this by passing the method name in theconstructor:defaultSizeTestCase = WidgetTestCase("testDefaultSize")resizeTestCase = WidgetTestCase("testResize")
16.
Running tests• Theunittest module contains a function called main, which can be used toeasily turn a test module into a script that will run the tests it contains.The main function uses the unittest.TestLoader class to automatically findand load test cases within the current module.• Therefore, if you name your test methods using the test* convention, youcan place the following code at the bottom of your test module:if __name__ == "__main__":unittest.main()