Differences between revisions 12 and 13
Revision 12 as of 2008-12-15 16:47:41
Size: 8108
Editor: mpiat1403
Comment:
Revision 13 as of 2008-12-16 11:03:04
Size: 8817
Editor: mpiat1403
Comment: Examples split into correct header/implementation files
Deletions are marked like this. Additions are marked like this.
Line 4: Line 4:

The project's homepage is http://sourceforge.net/projects/cppunit.
Line 28: Line 30:
// files Complex.h/Complex.cpp // file Complex.h
Line 37: Line 39:
}}}

{{{
// file Complex.cpp

#include "Complex.h"
Line 51: Line 59:
Now let us write a Cpp``Unit test case for this class:

{{{
// file ComplexNumberTest.h/.cpp
Now let us write a Cpp``Unit 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
Line 57: Line 68:
#include "Complex.h"
Line 61: Line 71:
  ComplexNumberTest( std::string name ) : CppUnit::TestCase( name ) {}
  
  void 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));
  }
  ComplexNumberTest( std::string name );
  void runTest();
Line 70: Line 75:


{{{
// 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:
Line 85: Line 110:
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.
Line 90: Line 114:
Suppose we put all the above code into one file `ComplexNumberTest.cpp`. Then the following makefile will do the job for us: The following makefile based on implicit rules will do the job for us:
Line 97: Line 121:
# Implicit rule: How to make an executable from a .cpp-file
%: %.cpp
 ${CC} -o $@ $< ${CXXFLAGS} ${INCLS} ${LIBDIRS} ${LIBS}
# 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
Line 103: Line 133:
Line 112: Line 141:
cc -o ComplexNumberTest ComplexNumberTest.cpp -I/var/tmp/cppunit/install/include/ -L/var/tmp/cppunit/install/lib -lcppunit -ldl
export LD_LIBRARY_PATH=/var/tmp/cppunit/install/lib && ./ComplexNumberTest
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
Line 118: Line 150:
/bin/sh: line 1:  1457 Aborted ./ComplexNumberTest /bin/sh: line 1: 10793 Aborted ./runComplexNumberTest
make: *** [runtest] Error 134

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].

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

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 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.

Here is a complete program:

// files ComplexNumberTestSuite.h/.cpp

// A test suite for Complex, run by a runner

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

#include "Complex.h"

class ComplexNumberTest : public CppUnit::TestFixture  {
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 );
  }

  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;
  }
};

// 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 );
  }
};

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)