9.3 Usage

     

Test classes are created by subclassing TestCase . The simplest approach is to override the method runTest( ) , as shown in Example 9-1.

Example 9-1. Simple unit test for the class Book
 booktests.py """Unit test for book.py""" import book import unittest class  BookTests  (unittest.TestCase):    def  runTest  (self):       """Test book creation"""       book1 = book.Book( "Cosmos", "Carl Sagan" )       self.  assertEqual  ( "Cosmos", book1.title ) 

This example creates the test class BookTests . The test method runTest( ) creates a Book and uses the assertEqual( ) test assert method to verify its attributes. Test methods customarily contain a label similar to the example's Test book creation . This test description is printed if the test fails.

The class Book tested by BookTests is given in Example 9-2.

Example 9-2. Simple unit test for the class Book
 book.py class  Book  :    title = ""    author = ""    def  __init_  _  (self, title, author):       self.title = title       self.author = author 

Unit tests may be run from the command line using unittests.py as a test runner. The argument specifies the Python module name, class name, and method name of the test to run. Example 9-3 demonstrates running BookTests this way and shows the result.

Example 9-3. Results of running BookTests
 $ python unittest.py booktests.BookTests.runTest . ------------------------------------------------ Ran 1 test in 0.000s OK 

For this command to work as shown, the module unittests.py must be present in the Python search path specified by the environment variable $PYTHONPATH .

Making BookTests fail by changing the test assert statement to self.assertEqual ( " Bad", book1.title ) demonstrates PyUnit's test failure reporting, as shown in Example 9-4.

Example 9-4. BookTests failure
 $ python unittest.py booktests.BookTests.runTest F ====================================================================== FAIL: Test book creation ---------------------------------------------------------------------- Traceback (most recent call last):   File "/cygdrive/c/work/UnitTestFrameworks/Python/example1/booktests.   py",  line 11, in runTest     self.assertEqual( "Bad", book1.title )   File "/cygdrive/c/work/UnitTestFrameworks/Python/example1/unittest.   py",  line 302, in failUnlessEqual     raise self.failureException, \ AssertionError: 'Bad' != 'Cosmos' ---------------------------------------------------------------------- Ran 1 test in 0.000s FAILED (failures=1) 

The failure report includes the test description Test book creation , as well as the specific assert condition that failed.

Rather than overriding runTest( ) , it is far more common to create uniquely named test methods. This allows building test classes with multiple test methods. Example 9-5 shows BookTests redesigned this way.

Example 9-5. Redesigned BookTest
 booktests.py """Unit test for book.py""" import book import unittest class  BookTests  (unittest.TestCase):    def  testCreateBook  (self):       """Test book creation"""       book1 = book.Book( "Cosmos", "Carl Sagan" )       self.assertEqual( "Cosmos", book1.title ) if __name__ == '__main_  _':    unittest.main( ) 

The additional two lines of code at the end allow the test to be run directly without using unittest.py , as shown in Example 9-6. All methods that have names starting with test are found and run.

Example 9-6. Running BookTest directly
 $ python booktests.py . --------------------- Ran 1 test in 0.000s OK 

With this approach, multiple test methods can be added to a test class. If they share objects, test fixture behavior should be implemented using the setUp() and tearDown( ) methods. Example 9-7 shows the test fixture LibraryTests .

Example 9-7. . The test class LibraryTests
 librarytests.py """Unit test for library.py""" import book import library import unittest class  LibraryTests  (unittest.TestCase):    def  setUp  (self):       self.library = library.Library( )       book1 = book.Book( "Cosmos", "Carl Sagan" )       self.library.addBook( book1 )       book2 = book.Book( "Contact", "Carl Sagan" )       self.library.addBook( book2 )    def  tearDown  (self):       self.library.dispose( )    def  testGetNumBooks  (self):       """Test getting number of books"""       self.  assert_  ( self.library.getNumBooks( )==2 )    def  testGetBook  (self):       """Test getting a book from library"""       book2 = self.library.getBook( "Cosmos" )       self.assertNotEqual( None, book2, "Book not found" ) 

The setUp( ) method creates a Library and adds two Book s to it, and tearDown( ) disposes of the Library . The test method testGetNumBooks( ) uses the test assert method assert_() to check the library's size . This is the most generic type of test assert, as it simply checks whether its argument evaluates to true .

Example 9-8 shows the Library class that is tested by LibraryTest .

Example 9-8. The Library class
 library.py class  NonexistentBookError  (Exception):    """Exception thrown for missing book"""    pass class  Library  :    """A library"""    def  __init_  _  (self):       self._  _books = dict( )    def  dispose  (self):       self._  _books.clear( )    def  addBook  (self, book):       """Add a book"""       self._  _books[book.title] = book    def  getBook  (self, title):       """Find a book by title"""       return self._  _books.get(title)    def  removeBook  (self, title):       """Remove a book"""       if self._  _books.has_key(title):          self._  _books.pop(title)       else:          raise NonexistentBookError    def  getNumBooks  (self):       """Get number of books"""       return len(self._  _books) 

Library uses the Python dictionary object dict( ) to contain a collection of Book s. The module library.py also defines the exception class NonexistentBookError . This type of exception is thrown by the method removeBook( ) if it cannot find the Book to remove.

The failUnlessRaises() test assert method can be used to check for expected exception behavior, as shown in Example 9-9.

Example 9-9. Testing for an expected exception
 librarytests.py    def  testRemoveNonexistentBook  (self):       """Test expected exception from removing a nonexistent book"""       self.  failUnlessRaises  (library.NonexistentBookError,          self.library.removeBook, "Nonexistent" ) 

The arguments passed to failUnlessRaises( ) are an exception type, a callable object, and a variable argument list. In this example, the exception type is NonexistentBookError and the callable object is the function removeBook( ) . The object is called with the specified argument list. If an exception of the given exception type is thrown, the test passes . If no exception is thrown, or some other type of error occurs, the test fails.

Multiple tests may be aggregated using TestSuite . Example 9-10 adds a function named suite() to create a TestSuite containing LibraryTest 's test methods and changes the call to run the suite to unittest.main() .

Example 9-10. Creating and running a TestSuite
 librarytests.py def  suite  ( ):    suite = unittest.  TestSuite  ( )    suite.  addTest  (LibraryTests("testGetNumBooks"))    suite.  addTest  (LibraryTests("testGetBook"))    suite.  addTest  (LibraryTests("testRemoveNonexistentBook"))    return suite if __name__ == '__main_  _':    unittest.main(defaultTest='suite') 

Tests are added to a TestSuite using its addTest( ) method. It also has an addTests( ) method that allows multiple tests to be added at once.

PyUnit provides a convenience method, makeSuite() , which creates a TestSuite . It finds all methods named with a given prefix, such as test , and returns a suite containing them. Example 9-11 demonstrates makeSuite( ) .

Example 9-11. . Using makeSuite( ) to create a TestSuite
 librarytests.py def  suite  ( ):    suite = unittest.  makeSuite  (LibraryTests, "test")    return suite 

It's often useful to create a module that builds TestSuite containing all the tests in each test class. Example 9-12 shows such a module, named alltests.py .

Example 9-12. Module to run all tests
 alltests.py import unittest def  suite  ( ):    modules_to_test = ('  booktests  ', '  librarytests  ')  alltests  = unittest.  TestSuite  ( )    for module in map(__import_  _, modules_to_test):       alltests.addTest(unittest.findTestCases(module))    return  alltests  if __name__ == '__main_  _':    unittest.main(defaultTest='suite') 

This example creates and runs a TestSuite named alltests that contains all the tests from booktests.py and librarytests.py .

Python includes a command-line interpreter for interactively running code. PyUnit tests can be run this way. Example 9-13 demonstrates using the interpreter to create and run a unit test.

Example 9-13. Running a test interactively
 $ python >>> import unittest >>> import librarytests >>> runner = unittest.TextTestRunner( ) >>> test = librarytests.LibraryTests("testGetBook") >>> runner.run(test) . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK <unittest._TextTestResult run=1 errors=0 failures=0> 

In this example, the modules unittest and librarytests are imported and a TextTestRunner is created. Next, a test containing the test method testGetBook( ) is created and run using the test runner.

A TestSuite can be created and run similarly, as shown in Example 9-14.

Example 9-14. Creating a TestSuite interactively
 >>> suite = unittest.makeSuite(librarytests.LibraryTests,'test') >>> runner.run(suite) ..... ---------------------------------------------------------------------- Ran 5 tests in 0.001s OK <unittest._TextTestResult run=5 errors=0 failures=0> 

Example 9-15 illustrates creating a test from BookTests and adding it to the TestSuite .

Example 9-15. Adding a test to the test suite
 >>> import booktests >>> test2 = booktests.BookTests("testCreateBook") >>> suite.addTest(test2) >>> runner.run(suite) ...... ---------------------------------------------------- Ran 6 tests in 0.001s OK <unittest._TextTestResult run=6 errors=0 failures=0> 

The PyUnit GUI is implemented in the module unittestgui.py . It is not included with the standard Python libraries but is available in downloads from the PyUnit web site. It acts as a test runner, running test modules, classes, and methods, and displays a friendly green or red test results indicator, as well as failure details. It is shown in Figure 9-1.

Figure 9-1. The PyUnit GUI
figs/utf_0901.gif

The name of the test to run is entered at the top. The name can specify a module (e.g., librarytests ), a test class ( librarytests.LibraryTests ), or a test method ( librarytests.LibraryTests.testGetBook ). Except when a specific test method name is specified, all the methods in the module or class being tested that have names starting with test are run.



Unit Test Frameworks
Unit Test Frameworks
ISBN: 0596006896
EAN: 2147483647
Year: 2006
Pages: 146
Authors: Paul Hamill

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net