White Box Testing
Test your code
   Home      UnitTestingWithCppUnit
 
CppUnit is a C++ unit testing framework. CppUnit is port of the JUnit testing framework developed by Erich Gamma and Kent Beck. The first port of JUnit to C++ was done by Michael Feathersv. They are os-specific, so Jerome Lacoste provided a port to Unix/Solaris.
Features:
  • XML output with hooks for additional data (XSL format avaliable in release 1.10.2 needs some FiXing)
  • Compiler-like text output to integrate with an IDE
  • Helper macros for easier test suite declaration
  • Hierarchical test fixture support
  • Test registry to reduce recompilation need
  • Test plug-in for faster compile/test cycle (self testable dynamic library)
  • Protector to encapsulate test execution (allow capture of exception not derived from std::exception)
  • MfcTestRunner
  • QtTestRunner, a Qt based graphic test runner
 

Building CppUnit

CppUnit must be built locally before developing the unit test. Following are the summarized step to build CppUnit on Unix environment
 
  1. Obtain the source and Unpack locally by unzipping and untarring the download.
  2. Generate make files by running ./configure.
  3. Compile it by calling make
  4. Install libraries and headers files with make install. 

platform-specific wikipage build instructions contributed by users Building CppUnit
 
Guide to getting MfcTestRunner working with VS6 Using CppUnit's MFCTestRunner with VS 6.0
 

 

Test Case

Test cases written as test method in fixture test class. Test Caller generate test cases from test method
void CalcTest::testAdd(){
  // Set up
  Calc *result = new Calc(15);
 
  //test
  CPPUNIT_ASSERT( *a + *b == *result);
}
 
 void CalcTest::testMinus(){
   //set up
   Calc *result = new Calc(5);
 
  //test
   CPPUNIT_ASSERT( *a - *b == *result);
}
 

Test Fixture

To run test cases independently test fixture is used. A test fixture is the set of preconditions or state needed for a test to run that serves as a base for a set of test cases.
 
To initialize resources required to run test, setUp() is used. And tearDown() is used for clean up of the acquired resource in setUp()
 
Test fixture serves as base for all the test cases of a class in which test cases defined. To define a test class fixture
  • Define base member variable for test class fixture
  • Override setup() method to initialize base member variable for test class fixture
  • Override teardown() method to release any resources allocated in setup()
Base member variable for fixture defined in CalcTest.h 
private:  Calc *a, *b, *c; 
 
setup() and teardown() overridden in CalcTest.cpp 
void CalcTest::setUp(){
          a = new Calc(10);
          b = new Calc(5);
          c = new Calc(10);
} 
 
void CalcTest::tearDown(){
         delete a;
         delete b;
         delete c;
}
 
 
To determine whether a method under test is performing correctly or not we use assertions. They tell you that some condition is true, equal, or not, and so on.
  • CPPUNIT_ASSERT(condition) Checks the condition is true and throws an exception if it's false.
  • CPPUNIT_ASSERT_MESSAGE(message, condition) Checks the condition is true and throws an exception and showing specified message if it is false.
  • CPPUNIT_Fail(message) Fails with the specified message.
  • CPPUNIT_ASSERT_EQUAL(expected,current) Checks whether the expected condition is the same as current, and raises an exception showing the expected and current values.
  • CPPUNIT_ASSERT_EQUAL_MESSAGE(message,expected,current) Checks whether the expected is the same as the actual, and raises an exception showing the expected and current values, and specified message.
  • CPPUNIT_ASSERT_DOUBLES_EQUAL(expected,current,delta) Macro for primitive double value comparisons.
  • CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(message,expected,current,delta) Macro for primitive double value comparisons, setting a user-supplied message in case of failure.
  • CPPUNIT_ASSERT_THROW(expression,ExceptionType) Asserts that the given expression throws an exception of the specified type
  • CPPUNIT_ASSERT_THROW_MESSAGE(message,expression,ExceptionType) Asserts that the given expression throws an exception of the specified type, setting a user supplied message in case of failure.
  • CPPUNIT_ASSERT_NO_THROW(expression) Asserts that the given expression does not throw any exceptions.
  • CPPUNIT_ASSERT_NO_THROW_MESSAGE(message,expression) Asserts that the given expression does not throw any exceptions, setting a user supplied message in case of failure.

Test Caller

Generate test case from test method in fixture test classCppUnit::TestCaller<ComplexNumberTest>("testAdd", &CalcTest::testAdd);

 

A test suite is a set of tests or test classes. To run a number of test cases define TestSuite and add test cases through addTest method.
 
//Define the suite
CppUnit::TestSuite suite;
 
//Add test cases
suite.addTest( new CppUnit::TestCaller<CalcTest>("testConstructor", &CalcTest::testConstructor ) );
suite.addTest( new CppUnit::TestCaller<CalcTest>("testAdd", &CalcTest::testAdd ) );
 
Test suite can have callers for other suites also
 
//Define the suite
CppUnit::TestSuite suite;
 
//Add test suite
suite.addTest( CalcTest::suite() );
suite.addTest( OtherTest::suite() );
 
 
TestResult manages TestListener. A single instance of TestResult required to run a suite. A TestResultCollector is a TestListener which collects the results of executing test cases.
 
To see results from TestResultCollector instance instantiate Outputter(CompilerOutputter) .
 
int main(int argc, char* argv[])
{
          //Define the suite
          CppUnit::TestSuite suite;
 
          //Define TestResult controler
          CppUnit::TestResult controler;
 
          // Add a listener that colllects test result
          CppUnit::TestResultCollector result;
          controler.addListener( &result );
 
          //Add test cases
          suite.addTest( new CppUnit::TestCaller<CalcTest>("testConstructor", &CalcTest::testConstructor ) );
          suite.addTest( new CppUnit::TestCaller<CalcTest>("testAdd", &CalcTest::testAdd ) );
          suite.addTest( new CppUnit::TestCaller<CalcTest>("testMinus", &CalcTest::testMinus) );
          suite.addTest( new CppUnit::TestCaller<CalcTest>("testEqual", &CalcTest::testEqual ) );
          suite.addTest( new CppUnit::ExceptionTestCaseDecorator<CalcOperatorError>(new CppUnit::TestCaller<CalcTest>("testQuotient", &CalcTest::testQuotient ) ) );
 
          //Run test suite
          suite.run( &controler );
 
          // Print test in a compiler compatible format.
          CppUnit::CompilerOutputter outputter( &result, std::cerr );
          outputter.write();
}
 
 
TestRunner used to run defined test suite and display their results. You need to define static method which returns suite and provide the returned suite to TestRunner
 
static CppUnit::Test *suite()
{
          //Define the suite
          CppUnit::TestSuite *suiteOfTests = new CppUnit::TestSuite( "CalcTest" );
                  
          //Add test cases
          suiteOfTests->addTest( new CppUnit::TestCaller<CalcTest>("testConstructor", &CalcTest::testConstructor ) );
          suiteOfTests->addTest( new CppUnit::TestCaller<CalcTest>("testAdd", &CalcTest::testAdd ) );
          suiteOfTests->addTest( new CppUnit::TestCaller<CalcTest>("testMinus", &CalcTest::testMinus) );
          suiteOfTests->addTest( new CppUnit::TestCaller<CalcTest>("testEqual", &CalcTest::testEqual ) );
          suiteOfTests->addTest( new CppUnit::ExceptionTestCaseDecorator<CalcOperatorError>(new CppUnit::TestCaller<CalcTest>("testQuotient", &CalcTest::testQuotient ) ) );
                  
          //Return test suite
          return suiteOfTests;
}
 
int main(int argc, char* argv[])
{
          // Adds the test to the list of test to run
          CppUnit::TextUi::TestRunner runner;
          runner.addTest( CalcTest::suite() );
 
          // Run the test.
          bool wasSucessful = runner.run();
 
          // Return error code 1 if the one of test failed.
          return wasSucessful ? 0 : 1;
}
 
 
Defining test suite is error prone. CppUnit provide helper macros to define static suite method.
 
  //Declare the suite
  CPPUNIT_TEST_SUITE( CalcTest );
 
  //Declare each test case
  CPPUNIT_TEST( testConstructor );
  CPPUNIT_TEST( testAdd );
  CPPUNIT_TEST( testMinus );
  CPPUNIT_TEST( testEqual );
  CPPUNIT_TEST_EXCEPTION( testQuotient, CalcOperatorError );
 
  //End the suite declaration
  CPPUNIT_TEST_SUITE_END();
 
 
TestFactoryRegistry solves the problem of forgetting to add test suite to the test runner
 
// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( CalcTest );
 
// Get the instance of registry Create
CppUnit::TestFactoryRegistry &registry =  CppUnit::TestFactoryRegistry::getRegistry();
 
// Adds the test to the list of test to run
CppUnit::TextUi::TestRunner runner;
runner.addTest( registry.makeTest() );
 
// Run the test.
bool wasSucessful = runner.run();
 
 
 
Sample Code 2 - Using TestRunner
 
Sample Code 3 - Using HelperMacros and TestFactoryRegistry
 

External References

 
 
CppUnit Tutorial by Zaid Towfic
 
Crash Course in using CppUnit