7.3 Usage

     

When writing unit tests using CppUnit, the general xUnit model is followed, as described in Chapter 3 and Chapter 6. Test objects are derived from TestCase and are run using a TestRunner . A number of code examples are given here to illustrate details of its usage. The examples are unit tests of a simple class named Book , shown in Example 7-1.

Example 7-1. The class Book
 Book.h using std::string; class  Book  {  public:    Book( string const &title )       : m_title( title ) {}    string getTitle( ) { return m_title; }  private:    string m_title; }; 

The simplest way to write a CppUnit unit test is to derive a class from TestCase and override its runTest() method. An example of such a unit test is given in Example 7-2.

Example 7-2. The simple test class BookTest
 BookTest.h #include "cppunit/TestCase.h" #include "Book.h" using std::string; class  BookTest  : public  CppUnit  ::  TestCase  {  public:    BookTest( string const &name ) : CppUnit::TestCase( name ) {}    void  runTest( )  {       Book book( "Cosmos" );  CPPUNIT_ASSERT  ( book.getTitle( ) == "Cosmos" );    } }; 

The test method runTest() creates a Book and uses the test assertion macro CPPUNIT_ASSERT() to test the title value. The test class is run by calling runTest( ) . The simple program in Example 7-3 illustrates this.

Example 7-3. A simple program to run the test class BookTest
 test.cpp #include <iostream> #include "BookTest.h" int main( ) {    try {  BookTest  test( "BookTest" );       test.  runTest  ( );       std::cout << "SUCCESS!" << std::endl;       return 0;    } catch ( ... ) {       std::cout << "FAILURE!\n" << std::endl;       return 1;    } } 

If runTest( ) throws an exception, the failure is reported . Here, the test succeeds:

 > ./test SUCCESS! 

The test can be made to fail by changing the title test value:

 CPPUNIT_ASSERT( book.getTitle( ) == "Moscow" ); 

The output reports the failure:

 > ./test FAILURE! 

The program test returns an error code of 1 in case of failure, which can be useful if an automated test script runs it.

This basic approach to writing unit tests is cumbersome. A new test class is implemented for each test method, and the runTest() method for each one must be called directly. The test results are reported only in a very simple way, as overall success or failure, with no details about what was tested and what failed.

The next step in increasing the sophistication of CppUnit test development is to implement unit tests as test fixtures, which allows multiple test methods to be implemented in one test class. Running them using a TestRunner provides nice reporting of the results.

An author attribute is added to Book in Example 7-4.

Example 7-4. Book with an author attribute
 Book.h using std::string; class  Book  {  public:    Book(string const &title, string const &author)       : m_title( title ),  m_author( author )  {}    string getTitle( ) { return m_title; }    string getAuthor( ) { return m_author; }  private:    string m_title;    string m_author; }; 

BookTest can have two test methods, one testing each attribute of Book . Since both test methods can test the same Book , it makes sense to implement BookTest as a fixture. Example 7-5 shows the new version of BookTest .

Example 7-5. BookTest written as a test fixture with two test methods
 BookTest.h #include "cppunit/TestCase.h" #include "Book.h" using std::string; class BookTest : public CppUnit::TestFixture {  private:    Book *book;  public:    void  setUp( )  {       book = new Book( "Cosmos", "Carl Sagan" );    }    void  tearDown( )  {       delete book;    }    void  testTitle  ( ) {  CPPUNIT_ASSERT_EQUAL  ( string("Cosmos"), book->getTitle( ) );    }    void  testAuthor  ( ) {  CPPUNIT_ASSERT_EQUAL  ( string("Carl Sagan"), book->getAuthor( ) );    } }; 

The fixture contains a test Book , which is created in setUp( ) and deleted in tearDown() . The two test methods are named testTitle( ) and testAuthor( ) . It is conventional to give test methods a name starting with "test".

The test assertion macro CPPUNIT_ASSERT_EQUAL() is used to test the value of the title and author attributes. This macro handles arguments of many common data types, including std::string , as shown in Example 7-5.

The test program can use the text TestRunner to run BookTest , as shown in Example 7-6.

Example 7-6. Using the text TestRunner to run BookTest
 test.cpp #include "cppunit/ui/text/TestRunner.h" #include "cppunit/TestCaller.h" #include "BookTest.h" int main( ) {  CppUnit  ::  TextUi  ::  TestRunner   runner  ;  runner.addTest  ( new CppUnit::TestCaller<BookTest>(                    "  testTitle  ",                    &BookTest::testTitle ) );  runner.addTest  ( new CppUnit::TestCaller<BookTest>(                    "  testAuthor  ",                    &BookTest::testAuthor ) );    if ( runner.run( ) )       return 0;    else       return 1; } 

A TestCaller is created for each test method and added to the TestRunner using its addTest( ) method. The TestRunner runs each test method as a separate testsetting up and tearing down the fixture each timeand then reports the test results:

 > ./test .. OK (2 tests) 

A failure can be produced in testAuthor( ) by changing the test condition:

 CPPUNIT_ASSERT_EQUAL( string("Anonymous"), book->getAuthor( ) ); 

The details of the failure are reported, including the test name and source code location for the assertion that failed:

 > ./test ..F !!!FAILURES!!! Test Results: Run:  2   Failures: 1   Errors: 0 1) test: testAuthor (F) line: 25 BookTest.h expected: Anonymous but was:  Carl Sagan 

As multiple text fixtures are developed, they can be added to a TestSuite , which aggregates them and makes running them a one-step operation. Example 7-7 demonstrates creating a TestSuite . The static method suite() is added to BookTest to return its suite of tests.

Example 7-7. Creating a TestSuite for BookTest's test methods
 BookTest.h    static CppUnit::Test *  suite  ( )    {       CppUnit::TestSuite *suite          = new CppUnit::TestSuite( "BookTest" );  suite->addTest  ( new CppUnit::TestCaller<BookTest>(                       "  testTitle  ",                       &BookTest::testTitle ) );  suite->addTest  ( new CppUnit::TestCaller<BookTest>(                       "  testAuthor  ",                       &BookTest::testAuthor ) );       return suite;    } 

The code to run the tests is simplified when they are run as a suite, as shown in Example 7-8.

Example 7-8. Running the test suite with the TestRunner
 test.cpp #include "cppunit/ui/text/TestRunner.h" #include "cppunit/TestCaller.h" #include "BookTest.h" int main( ) {    CppUnit::TextUi::TestRunner runner;    runner.addTest(  BookTest::suite( )  );    if ( runner.run( ) )       return 0;    else       return 1; } 

CppUnit includes helper macros that eliminate some of the coding effort by automatically creating TestSuite objects. The static method suite( ) in BookTest can be replaced by a series of macro calls, as shown in Example 7-9.

Example 7-9. Using helper macros to replace the suite( ) method
 BookTest.h    CPPUNIT_TEST_SUITE(  BookTest  );    CPPUNIT_TEST(  testTitle  );    CPPUNIT_TEST(  testAuthor  );    CPPUNIT_TEST_SUITE_END( ); 

The macro call CPPUNIT_TEST_SUITE() creates the static method suite() to return a TestSuite . The CPPUNIT_TEST( ) calls add the two test methods to the suite. The call CPPUNIT_TEST_SUITE_END() is necessary to end the declaration of the suite.

The macro CPPUNIT_TEST_EXCEPTION( ) adds a test method that is expected to throw an exception to the test suite. The test passes if the expected exception is thrown. To demonstrate this, the constructor for Book is modified to throw an std::exception if the title is empty. Example 7-10 shows the new test method for this behavior, testInvalidTitle( ) , and shows how it is added to the test suite.

Example 7-10. Using CPPUNIT_TEST_EXCEPTION to add an expected exception test
 BookTest.h    void  testInvalidTitle( ) throw (std::exception)  {       Book *badBook = new Book( "", "Mark Twain" );    }    CPPUNIT_TEST_SUITE( BookTest );    CPPUNIT_TEST( testTitle );    CPPUNIT_TEST( testAuthor );    CPPUNIT_TEST_EXCEPTION( testInvalidTitle, std::exception );    CPPUNIT_TEST_SUITE_END( ); 

Example 7-11 shows the Book constructor modified to throw an std::exception .

Example 7-11. Book constructor modified to throw exception
 Book.h  Book  (string const &title, string const &author)       : m_title( title ), m_author( author )    {       if ( m_title.empty( ) )          throw std::exception( );    } 

When run, testInvalidTest( ) creates a Book with an empty title, the constructor throws the expected exception, and the test passes.

The similar macro CPPUNIT_TEST_FAILURE( ) adds a test that is expected to fail to the test suite, as shown in Example 7-12.

Example 7-12. Adding an expected failure test to the test suite
 BookTest.h    void  testAlwaysFails  ( ) {       CPPUNIT_FAIL( "Expected failure" );    }    CPPUNIT_TEST_SUITE( BookTest );    CPPUNIT_TEST_FAIL(  testAlwaysFails  );    CPPUNIT_TEST_SUITE_END( ); 

Another useful helper macro is CPPUNIT_TEST_SUITE_REGISTRATION() . It is used to register test suites with the TestFactoryRegistry , as shown in Example 7-13.

Example 7-13. Registering a test suite and using the registry to create a test
 test.cpp #include "cppunit/ui/text/TestRunner.h" #include "cppunit/TestCaller.h" #include "BookTest.h" int main( ) {  CPPUNIT_TEST_SUITE_REGISTRATION  ( BookTest );    CppUnit::TextUi::TestRunner runner;    CppUnit::TestFactoryRegistry &registry       = CppUnit::TestFactoryRegistry::getRegistry( );    runner.addTest(  registry.makeTest( )  );    if ( runner.run( ) )       return 0;    else       return 1; } 

The registry automatically creates a TestSuite containing all of the registered tests. The call registry.makeTest( ) returns the TestSuite to run. This feature is particularly useful when there are many tests and writing the code to add them all to a TestSuite would be tedious .

The related helper macro CPPUNIT_TEST_SUITE_NAMED_REGISTRATION() is used to add TestSuite objects to a named registry. The named registry is used to create a TestSuite containing its set of registered tests. This allows tests to be divided into groups that may be run separately.



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