• No results found

1    Part 1 ­­ Beginning Python

1.9    Special Tasks

1.9.3    Unit tests

For more documentation on the unit test framework, see unittest ­­ Unit testing  framework ­­ http://docs.python.org/2/library/unittest.html#module­unittest For help and more information do the following at the Python interactive prompt:

>>> import unittest

>>> help(unittest)

And, you can read the source: Lib/unittest.py in the Python standard library.

1.9.3.1   A simple example

Here is a very simple example. You can find more information about this primitive way  of structuring unit tests in the library documentation for the unittest module Basic  example ­­ http://docs.python.org/lib/minimal­example.html

import unittest

class UnitTests02(unittest.TestCase):

    def testFoo(self):

        self.failUnless(False)

class UnitTests01(unittest.TestCase):

    def testBar01(self):

        self.failUnless(False)     def testBar02(self):

        self.failUnless(False) def main():

    unittest.main()

if __name__ == '__main__':

    main()

Notes:

The call to unittest.main() runs all tests in all test fixtures in the module. It actually creates an instance of class TestProgram in module 

Lib/unittest.py, which automatically runs tests.

Test fixtures are classes that inherit from unittest.TestCase.

Within a test fixture (a class), the tests are any methods whose names begin with  the prefix "test".

In any test, we check for success or failure with inherited methods such as  failIf(), failUnless(), assertNotEqual(), etc. For more on these 

methods, see the library documentation for the unittest module TestCase  Objects ­­ http://docs.python.org/lib/testcase­objects.html.

If you want to change (1) the test method prefix or (2) the function used to sort  (the order of) execution of tests within a test fixture, then you can create your own instance of class unittest.TestLoader and customize it. For example:

def main():

    my_test_loader = unittest.TestLoader()     my_test_loader.testMethodPrefix = 'check'

    my_test_loader.sortTestMethodsUsing = my_cmp_func     unittest.main(testLoader=my_test_loader)

if __name__ == '__main__':

    main()

But, see the notes in section Additional unittest features for instructions on a  (possibly) better way to do this.

1.9.3.2   Unit test suites

Here is another, not quite so simple, example:

#!/usr/bin/env python import sys, popen2 import getopt import unittest

class GenTest(unittest.TestCase):

    def test_1_generate(self):

        cmd = 'python ../generateDS.py ­f ­o out2sup.py ­s out2sub.py people.xsd'

        outfile, infile = popen2.popen2(cmd)         result = outfile.read()

        outfile.close()         infile.close()

        self.failUnless(len(result) == 0)     def test_2_compare_superclasses(self):

        cmd = 'diff out1sup.py out2sup.py'         outfile, infile = popen2.popen2(cmd)         outfile, infile = popen2.popen2(cmd)         result = outfile.read()

        outfile.close()         infile.close()

        #print 'len(result):', len(result)

        # Ignore the differing lines containing the date/time.

        #self.failUnless(len(result) < 130 and  result.find('Generated') > ­1)

        self.failUnless(check_result(result))     def test_3_compare_subclasses(self):

        cmd = 'diff out1sub.py out2sub.py'         outfile, infile = popen2.popen2(cmd)         outfile, infile = popen2.popen2(cmd)         result = outfile.read()

        outfile.close()         infile.close()

        # Ignore the differing lines containing the date/time.

        #self.failUnless(len(result) < 130 and  result.find('Generated') > ­1)

        self.failUnless(check_result(result))

def check_result(result):

    flag1 = 0     flag2 = 0

    lines = result.split('\n')     len1 = len(lines)

    if len1 <= 5:

        flag1 = 1

    s1 = '\n'.join(lines[:4])     if s1.find('Generated') > ­1:

        flag2 = 1

    return flag1 and flag2

# Make the test suite.

def suite():

    # The following is obsolete.  See Lib/unittest.py.

    #return unittest.makeSuite(GenTest)     loader = unittest.TestLoader()     # or alternatively

    # loader = unittest.defaultTestLoader

    testsuite = loader.loadTestsFromTestCase(GenTest)     return testsuite

# Make the test suite and run the tests.

def test():

    testsuite = suite()

    runner = unittest.TextTestRunner(sys.stdout, verbosity=2)     runner.run(testsuite)

USAGE_TEXT = """

Usage:

    python test.py [options]

Options:

    ­h, ­­help      Display this help message.

Example:

    python test.py

"""

def usage():

    print USAGE_TEXT     sys.exit(­1)

def main():

    args = sys.argv[1:]

    try:

        opts, args = getopt.getopt(args, 'h', ['help'])     except:

        usage()     relink = 1

    for opt, val in opts:

        if opt in ('­h', '­­help'):

      usage()     if len(args) != 0:

        usage()     test()

if __name__ == '__main__':

    main()     #import pdb

    #pdb.run('main()')

Notes:

GenTest is our test suite class. It inherits from unittest.TestCase.

Each method in GenTest whose name begins with "test" will be run as a test.

The tests are run in alphabetic order by method name.

Defaults in class TestLoader for the test name prefix and sort comparison  function can be overridden. See 5.3.8 TestLoader Objects ­­ 

http://docs.python.org/lib/testloader­objects.html.

A test case class may also implement methods named setUp() and 

tearDown() to be run before and after tests. See 5.3.5 TestCase Objects ­­ 

http://docs.python.org/lib/testcase­objects.html. Actually, the first test method in  our example should, perhaps, be a setUp() method.

The tests use calls such as self.failUnless() to report errors. These are  inherited from class TestCase. See 5.3.5 TestCase Objects ­­ 

http://docs.python.org/lib/testcase­objects.html.

Function suite() creates an instance of the test suite.

Function test() runs the tests.

1.9.3.3   Additional unittest features

And, the following example shows several additional features. See the notes that follow 

the code:

import unittest

class UnitTests02(unittest.TestCase):

    def testFoo(self):

        self.failUnless(False)     def checkBar01(self):

        self.failUnless(False)

class UnitTests01(unittest.TestCase):

    # Note 1

    def setUp(self):

        print 'setting up UnitTests01'     def tearDown(self):

        print 'tearing down UnitTests01'     def testBar01(self):

        print 'testing testBar01'         self.failUnless(False)     def testBar02(self):

        print 'testing testBar02'         self.failUnless(False) def function_test_1():

    name = 'mona'

    assert not name.startswith('mo') def compare_names(name1, name2):

    if name1 < name2:

        return 1

    elif name1 > name2:

        return ­1     else:

        return 0 def make_suite():

    suite = unittest.TestSuite()     # Note 2

    suite.addTest(unittest.makeSuite(UnitTests01,  sortUsing=compare_names))

    # Note 3

    suite.addTest(unittest.makeSuite(UnitTests02, prefix='check'))     # Note 4

    suite.addTest(unittest.FunctionTestCase(function_test_1))     return suite

def main():

    suite = make_suite()

    runner = unittest.TextTestRunner()     runner.run(suite)

if __name__ == '__main__':

    main()

Notes:

1. If you run this code, you will notice that the setUp and tearDown methods in  class UnitTests01 are run before and after each test in that class.

2. We can control the order in which tests are run by passing a compare function to  the makeSuite function. The default is the cmp built­in function.

3. We can control which methods in a test fixture are selected to be run by passing  the optional argument prefix to the makeSuite function.

4. If we have an existing function that we want to "wrap" and run as a unit test, we  can create a test case from a function with the FunctionTestCase function. If we do that, notice that we use the assert statement to test and possibly cause  failure.

1.9.3.4   Guidance on Unit Testing

Why should we use unit tests? Many reasons, including:

Without unit tests, corner cases may not be checked. This is especially important,  since Python does relatively little compile time error checking.

Unit tests facilitate a frequent and short design and implement and release  development cycle. See ONLamp.com ­­ Extreme Python ­­ 

http://www.onlamp.com/pub/a/python/2001/03/28/pythonnews.html and What is  XP ­­ http://www.xprogramming.com/what_is_xp.htm.

Designing the tests before writing the code is "a good idea".

Additional notes:

In a test class, instance methods setUp and tearDown are run automatically  before each and after each individual test.

In a test class, class methods setUpClass and tearDownClass are run  automatically once before and after all the tests in a class.

Module level functions setUpModule and tearDownModule are run before  and after any tests in a module.

In some cases you can also run tests directly from the command line. Do the  following for help:

$ python ­m unittest ­­help