Differences between revisions 13 and 14
Revision 13 as of 2008-12-16 11:03:04
Size: 8817
Editor: mpiat1403
Comment: Examples split into correct header/implementation files
Revision 14 as of 2008-12-16 12:00:35
Size: 9862
Editor: mpiat1403
Comment: Better description of test fixtures and suites
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:

'''Index'''
[[TableOfContents]]
Line 159: Line 163:
Now go on and read the rest of the [http://cppunit.sourceforge.net/doc/lastest/cppunit_cookbook.html CppUnit CookBook] and try out the examples given there. You will learn about Test``Fixtures, Test``Callers, Test``Suites, and Test``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 Test``Fixtures, Test``Callers, Test``Suites, and Test``Runners.
Line 171: Line 177:
Here is a complete program:

{{{
// files ComplexNumberTestSuite.h/.cpp

// A test suite for Complex, run by a runner
In a fixture, we can

 * Add member variables for each part of the fixture
 * Override setUp() to initialize the variables
 * Override tearDown() to release any permanent resources we allocated in setUp()

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.

{{{
// file ComplexNumberTestFixture.h

// A test fixture for Complex, returning a test suite to be run by a runner
Line 184: Line 196:
class ComplexNumberTest : public CppUnit::TestFixture { 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 name of the test case that failed
 * The name of the source file that contains the test
 * The line number where the failure occurred
 * All of the text inside the call to `CPPUNIT_ASSERT()` which detected the failure

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 `ComplexNumberTest` using these macros:

{{{
// file ComplexNumberTestWithMacro.h/.cpp

#include <cppunit/extensions/HelperMacros.h>

#include "Complex.h"

class ComplexNumberTestWithMacro : public CppUnit::TestFixture {

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

// remainder as in the class above
Line 212: Line 346:

  static CppUnit::Test *suite()
  {
    CppUnit::TestSuite *suiteOfTests = new CppUnit::TestSuite( "ComplexNumberTest" );

    suiteOfTests->addTest( new CppUnit::TestCaller<ComplexNumberTest>(
                                   "testEquality",
                                   &ComplexNumberTest::testEquality ) );
    suiteOfTests->addTest( new CppUnit::TestCaller<ComplexNumberTest>(
                                   "testAddition",
                                   &ComplexNumberTest::testAddition ) );
    return suiteOfTests;
  }
Line 228: Line 349:
{{{
// file runTestSuite.cpp

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

int main()
{
  CppUnit::TextUi::TestRunner runner;
  runner.addTest( ComplexNumberTest::suite() );
  //runner.addTest( AnotherTestFixture::suite() );
  //runner.addTest( AnExampleTestCase::suite() );
  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 name of the test case that failed
 * The name of the source file that contains the test
 * The line number where the failure occurred
 * All of the text inside the call to `CPPUNIT_ASSERT()` which detected the failure

The output of the above program is

{{{
..F


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


1) test: testAddition (F) line: 56 ComplexNumberTestSuite.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 `ComplexNumberTest` using these macros:

{{{
// file ComplexNumberTestWithMacro.h/.cpp

#include <cppunit/extensions/HelperMacros.h>

#include "Complex.h"

class ComplexNumberTestWithMacro : public CppUnit::TestFixture {

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

// remainder as in the class above

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

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

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

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

This document describes very shortly how to install the [http://sourceforge.net/projects/cppunit/ CppUnit] framework under Unix and how to write and run a very simplistic test case. This should get you started with using CppUnit, so that you can follow and try out the examples given in the [http://cppunit.sourceforge.net/doc/lastest/cppunit_cookbook.html CppUnit CookBook].

Index TableOfContents

Installing

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

Download the latest version of CppUnit from 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
        ${CC} -c $< ${CXXFLAGS} ${INCLS}

# Implicit rule: How to make an executable from .o-files
%: %.o
        ${CC} -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:

  • A TestFixture is a set of objects that serves as a base for a set of test cases, together with methods that implement these test cases.

  • A TestCaller runs a particular method (test case) of a fixture.

  • A TestSuite runs any number of callers together.

  • A TestRunner runs a suite and displays its results.

In a fixture, we can

  • Add member variables for each part of the fixture
  • Override setUp() to initialize the variables
  • Override tearDown() to release any permanent resources we allocated in setUp()

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.

// 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 name of the test case that failed
  • The name of the source file that contains the test
  • The line number where the failure occurred
  • All of the text inside the call to CPPUNIT_ASSERT() which detected the failure

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 ComplexNumberTest using these macros:

// file ComplexNumberTestWithMacro.h/.cpp

#include <cppunit/extensions/HelperMacros.h>

#include "Complex.h"

class ComplexNumberTestWithMacro : public CppUnit::TestFixture  {

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

// remainder as in the class above

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

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

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

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

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

In the Cookbook, read the sections on the TestFactoryRegistry (which solves the problem of forgetting to add a fixture suite to the test runner - since it is in another file, it is easy to forget) and the section on how to automate post-build checks.

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