Index TableOfContents

About this document

This document describes in brief how to install the [http://sourceforge.net/projects/cppunit/ CppUnit] framework under Unix and how to write and run test programs with it. This should get you started with using CppUnit.

The document is based on the [http://cppunit.sourceforge.net/doc/lastest/cppunit_cookbook.html CppUnit CookBook]. The examples and many explanations are taken almost verbatim from there, slightly changed and improved for readability.

References

The project's homepage is http://sourceforge.net/projects/cppunit.

The latest Doxygen documentation of the project's class hierarchy is http://cppunit.sourceforge.net/doc/lastest/index.html. This may be useful while you read this document.

Installing

Download the latest version of CppUnit from http://sourceforge.net/projects/cppunit. At the time of this writing, it is http://downloads.sourceforge.net/cppunit/cppunit-1.12.1.tar.gz.

Let us install it under /var/tmp/cppunit/install:

$ cd /var/tmp/
$ mv ~/cppunit-1.12.1.tar.gz .
$ tar xzf cppunit-1.12.1.tar.gz
$ ln -s cppunit-1.12.1 cppunit
$ cd cppunit
$ ./configure --prefix=`pwd`/install
$ make
$ make check
$ make install

Writing a simplistic test case

Suppose we are developing a class Complex for dealing with complex numbers. In the following code, the operator+ intentionally contains a bug:

// file Complex.h

class Complex { 
  friend bool   operator==(const Complex& a, const Complex& b);
  friend Complex operator+(const Complex& a, const Complex& b);
  double real, imaginary;
public:
  Complex( double r, double i = 0 )  : real(r), imaginary(i) {}
};

// file Complex.cpp

#include "Complex.h"

bool operator==( const Complex &a, const Complex &b )
{ 
  return a.real == b.real  &&  a.imaginary == b.imaginary; 
}

Complex operator+( const Complex &a, const Complex &b )
{ 
  // BUG! imaginary part should be 'a.imaginary + b.imaginary'
  return Complex(a.real + b.real, a.imaginary + b.real); 
}

Now let us write a CppUnit test case for this class.

For this, we have to subclass the TestCase class and to override the method runTest(). To check a value, we call CPPUNIT_ASSERT(bool) and pass in an expression that is true if the test succeeds.

// file ComplexNumberTest.h

#include <cppunit/TestCase.h>

class ComplexNumberTest : public CppUnit::TestCase { 
public: 
  ComplexNumberTest( std::string name );
  void runTest(); 
};

// file ComplexNumberTest.cpp

#include "Complex.h"
#include "ComplexNumberTest.h"

ComplexNumberTest::ComplexNumberTest( std::string name ) : CppUnit::TestCase( name ) {}

void ComplexNumberTest::runTest() {
  CPPUNIT_ASSERT( Complex (10, 1) == Complex (10, 1) );
  CPPUNIT_ASSERT( !(Complex (1, 1) == Complex (2, 2)) );
  CPPUNIT_ASSERT( Complex (1, 2) + Complex(3, 4) == Complex (4, 6));
}

The main program creates a TestCase object and calls its runTest() method:

// file runComplexNumberTest.cpp

#include "ComplexNumberTest.h"

int main()
{
  ComplexNumberTest myTest("My first CppUnit test");
  
  myTest.runTest();
}

Compiling and running

The following makefile based on implicit rules will do the job for us:

INCLS   = -I/var/tmp/cppunit/install/include/
LIBDIRS = -L/var/tmp/cppunit/install/lib
LIBS    = -lcppunit -ldl # note that CppUnit needs libld.so!

# Implicit rule: How to make an .o-file from a .cpp-file
%.o: %.cpp
        ${CXX} -c $< ${CXXFLAGS} ${INCLS}

# Implicit rule: How to make an executable from .o-files
%: %.o
        ${CXX} -o $@ $^ ${LIBDIRS} ${LIBS}

runComplexNumberTest: runComplexNumberTest.o ComplexNumberTest.o Complex.o

runtest: runComplexNumberTest
        export LD_LIBRARY_PATH=/var/tmp/cppunit/install/lib && ./runComplexNumberTest

Note that CppUnit needs the library ld.so in the linking step.

Now we can compile and run the program:

$ make runtest
cc -c runComplexNumberTest.cpp -Wall -I/var/tmp/cppunit/install/include/
cc -c ComplexNumberTest.cpp -Wall -I/var/tmp/cppunit/install/include/
cc -c Complex.cpp -Wall -I/var/tmp/cppunit/install/include/
cc -o runComplexNumberTest runComplexNumberTest.o ComplexNumberTest.o Complex.o -L/var/tmp/cppunit/install/lib -lcppunit -ldl
export LD_LIBRARY_PATH=/var/tmp/cppunit/install/lib && ./runComplexNumberTest
terminate called after throwing an instance of 'CppUnit::Exception'
  what():  assertion failed
- Expression: Complex (1, 2) + Complex(3, 4) == Complex (4, 6)

/bin/sh: line 1: 10793 Aborted                 ./runComplexNumberTest
make: *** [runtest] Error 134

As we can see, the third assertion failed, which detects the bug in our operator+.

Fixtures, Callers, Suites, and Runners

That was a very simple test. Ordinarily, we will have many little test cases that we will want to run on the same set of objects. To do this, we can use a "fixture".

Now you should maybe skim through the [http://cppunit.sourceforge.net/doc/lastest/cppunit_cookbook.html CppUnit CookBook]. You will learn about TestFixtures, TestCallers, TestSuites, and TestRunners.

In short:

In a fixture, we can

Here is a test fixture for our Complex class. It allocates three Complex objects to be used in its test cases. It implements two test cases, testEquality() and testAddition(). It overrides the suite() method to create and return a test suite consisting of its two test cases.

setup() and tearDown() are called before and after every test case in the fixture, respectively.

// file ComplexNumberTestFixture.h

// A test fixture for Complex, returning a test suite to be run by a runner

#include <cppunit/TestFixture.h>
#include <cppunit/TestSuite.h>
#include <cppunit/TestCaller.h>

#include "Complex.h"

class ComplexNumberTestFixture : public CppUnit::TestFixture  {
private:
  Complex *a, *b, *s;
public:
  void setUp();
  void tearDown();

  void testEquality();
  void testAddition();

  static CppUnit::Test* suite();
};

// file ComplexNumberTestFixture.cpp

#include "ComplexNumberTestFixture.h"

void ComplexNumberTestFixture::setUp()
{
  a = new Complex( 1, 2 );
  b = new Complex( 3, 4 );
  s = new Complex( 4, 6 );  
}

void ComplexNumberTestFixture::tearDown() 
{
  delete a;
  delete b;
  delete s;
}

void ComplexNumberTestFixture::testEquality()
{
  CPPUNIT_ASSERT( *a == *a );
  CPPUNIT_ASSERT( !(*a == *b) );
}

void ComplexNumberTestFixture::testAddition()
{
  CPPUNIT_ASSERT( *a + *b == *s );
}

CppUnit::Test* ComplexNumberTestFixture::suite()
{
  CppUnit::TestSuite *suiteOfTests = new CppUnit::TestSuite( "ComplexNumberTest" );
  
  suiteOfTests->addTest( new CppUnit::TestCaller<ComplexNumberTestFixture>( 
                                "testEquality", 
                                &ComplexNumberTestFixture::testEquality ) );
  suiteOfTests->addTest( new CppUnit::TestCaller<ComplexNumberTestFixture>(
                                "testAddition",
                                &ComplexNumberTestFixture::testAddition ) );
  return suiteOfTests;
}

To run test the suite returned by the fixture's suite() method, we pass it to a TestRunner object and call its run() methdod:

// file runTestSuite.cpp

#include <cppunit/ui/text/TestRunner.h>
#include "ComplexNumberTestFixture.h"

int main()
{
  CppUnit::TextUi::TestRunner runner;
  runner.addTest( ComplexNumberTestFixture::suite() );
  //runner.addTest( AnotherTestFixture::suite() );
  //runner.addTest( AnExampleTestCase::suite() );
  // and maybe add more suites
  runner.run();
}

The TestRunner will run the tests. If all the tests pass, we shall get an informative message. If any fail, we shall get the following information:

The output of the above program is

..F


!!!FAILURES!!!
Test Results:
Run:  2   Failures: 1   Errors: 0


1) test: testAddition (F) line: 27 ComplexNumberTestFixture.cpp
assertion failed
- Expression: *a + *b == *s

Helper macros

Implementing the static suite() method in a TestFixture is a repetitive and error prone task. A set of macros have been created to automatically implement this method.

The following code is a rewrite of ComplexNumberTestFixture using these macros:

// file ComplexNumberTestFixturewithMacro.h

// A test fixture for Complex, returning a test suite to be run by a runner

#include <cppunit/TestFixture.h>
#include <cppunit/TestSuite.h>
#include <cppunit/TestCaller.h>

#include <cppunit/extensions/HelperMacros.h>

#include "Complex.h"

class ComplexNumberTestFixtureWithMacro : public CppUnit::TestFixture  {

CPPUNIT_TEST_SUITE( ComplexNumberTestFixtureWithMacro );
CPPUNIT_TEST( testEquality ); 
CPPUNIT_TEST( testAddition );
CPPUNIT_TEST_SUITE_END(); 

// remainder as in the class above, but without suite()

private:
  Complex *a, *b, *s;
public:
  void setUp();
  void tearDown();

  void testEquality();
  void testAddition();
};

// of course, the implementation of suite() is also missing in the .cpp file ;-)

There are other helper macros, for example, to check that a specific exception is thrown under specific circumstances. Check the documentation.

Test factory registry

It is easy to forget to add a fixture suite to the test runner since it is in another file.

Compilation bottleneck may be caused by the inclusion of all test case headers.

To solve these problems, CppUnit introduced TestFactoryRegistry objects. Such an object is a place where suites can be registered at initialization time.

For example, to register the ComplexNumberTestFixture suite in the registry, we would add 2 lines in the .cpp file:

// file ComplexNumberTestFixture.cpp

#include <cppunit/extensions/HelperMacros.h> 
#include "ComplexNumberTestFixture.h"

CPPUNIT_TEST_SUITE_REGISTRATION( ComplexNumberTestFixture ); 

// now as before...

Then, to run the tests using the text test runner, we do not need to include the fixture anymore:

// file runTestSuiteWithTestFactoryRegistry.cpp

#include <cppunit/extensions/TestFactoryRegistry.h> 
#include <cppunit/ui/text/TestRunner.h> 

int main() 
{ 
  CppUnit::TextUi::TestRunner runner;
  CppUnit::TestFactoryRegistry &registry 
       = CppUnit::TestFactoryRegistry::getRegistry();
  runner.addTest( registry.makeTest() ); 
  runner.run(); 
} 

That is, first we retrieve the instance of the TestFactoryRegistry. Then we obtain and add a new TestSuite created by the TestFactoryRegistry that contains all the test suites registered using CPPUNIT_TEST_SUITE_REGISTRATION().

You may wonder from where the runner now knows what tests to add: This is specified by simply linking the ComplexNumberTestFixture.o object file to the main object file runTestSuiteWithTestFactoryRegistry.o.

Automated post-build checks

How do we integrate unit testing to our build process? TestRunner::run() returns a boolean indicating wether the run was successful. We can just return this value from main():

#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>

int main()
{
  CppUnit::TextUi::TestRunner runner;
  CppUnit::TestFactoryRegistry &registry 
       = CppUnit::TestFactoryRegistry::getRegistry();
  runner.addTest( registry.makeTest() );
  bool wasSuccessful = runner.run( "", false );
  return wasSuccessful ? 0 : 1;
}

Now, we can run our test program after each compilation of our tested application. This can be formulated by an additional command in the makefile. If there is an error (the exit status is nonzero), the make utility gives up on the current rule, and perhaps on all rules.

CompleteSearch: completesearch/InstallingAndRunningCppUnit (last edited 2008-12-17 12:00:29 by mpiat1403)