Cpp Unit, Windows Edition


Lastest changes

Content

Overview
Requirement
Licensing
Download
Questions, comments and bugs reports
Getting Started
    Compiling the samples
    Using the test runner
    Creating test case
        The way of macros
        The way of templates
    Using TestRunner from your application
    Compiling with CppUnit and TestRunner
Known bugs

Overview

    CppUnit is a C++ unit testing framework base on junit. See the links below for details.
    CppUnit can be found at http://sourceforge.net/projects/cppunit/.It is based on a version that was Windows specific (can be found at http://www.xprogramming.org). There was many features I wanted to add to CppUnit.
    The version of CppUnit you can found here is what the sourceforge release will look like once everything is integrated. At the current only the core cppunit lib have been integrated. The patch for the testrunner (original version) has been submitted. I'm waiting the integration of this one to post the others patches (TestSuiteBuilder).

    The changes made to CppUnit are:

Requirement

Licensing

CppUnit is covered by the Gnu Public License.

Download

    As this version of CppUnit is temporary, I'll named it "CppUnitW".

CppUnitW 1.2: includes source and documentation for VC++ and Unix. (200Ko).
CppUnitW 1.1: includes source and documentation. (233Ko).

Questions, comments and bugs reports

    Questions, comments and bugs reports are more than welcome. You can reach me at the following address: gaiacrtn@free.fr
    Features would be better discussed on the cppunit mailing list (follow the link and click on list).

Getting Started

Compiling the samples

Download and unzip your version of CppUnitW.

The physical layout is as follow:

CppUnit/:                The directory contained in the zip file.
  doc/:                  Contains some documentation about unit testing.
    index.html           This page (without images).
  examples/:             Some examples.
    Example.dsw          VC++ workspace for the hierarchy example.
    hierarchy/:          Source of a text test runner based example.
    msvc6/:              VC++ specific examples (use the graphic TestRunner)
      HostApp/:          Source of a graphic test runner based example.
        HostApp.dsw      Workspace to compile the graphic test runner based example.
  Lib/:                  Target directory for the compiled dll and libraries.
  src/:                  Source (cppunit should move there...)
    cppunit/:            Source for the CppUnit library and private headers.
      CppUnit.dsw        Workspace to compile the cppunit static library.
    msvc6/:              Source specific to VC++ platform.
      TestRunner/:       Source of the graphic test runner dynamic library.
        TestRunner.dsw   Workspace to compile the graphic test runner.
Now, to run your first sample:
  1. Your lucky:

  2. Open HostApp.dsw with VC++. Make the HostApp the active project (right click on the project in the file tab). Run the project, the dependencies setting should automatically compile the cppunitd.lib and testrunnerd.dll.
  3. It's not your day:
  4. Using the test runner

    Well, you can now run the HostApp example you just compiled. You should see something like this:

    The top combo box show the most recently used test. You select a test using the button Browse (that's a new feature). When a test is run using the Run button, the color of the progress bar indicates if a test has failed (red) or not (green). The list below show details about the failed test.
    The autorun check box indicates is the most recently used test must be automatically run when the test runner is opened.
    All those settings are stored in the host application registry in the CppUnit section (allowing per application setting and more recently used test list).
    Pressing the spacebar will run the selected test.
    Pressing 'Q' will send a WM_QUIT message to the host application, which should result in exiting the host application.

    The above dialog appears when you click on Browse button. You select the test you want to run with this dialog. You can explore the hierarchy of tests.

Creating test case

    One of the major improvement of this version over other is the creation of test case.

    For the first comer who had never used cppunit, I would recommend to first try using the macro, since it's easier to set up, then switch to the template helper such as TestSuiteBuilder once they get a feeling of how it works. Macros makes it easy, but you can't build upon the existing framework using them.

The way of macros

    First, you must create a class that inherit CppUnit::TestCase, the base class for all test case:
#include <cppunit/TestCase.h>
#include <cppunit/extensions/HelperMacros.h>
class ExampleTestCase : public CppUnit::TestCase {
    Then, you declares the test suite for this test case, and all the methods to run for this test case:
  CU_TEST_SUITE( ExampleTestCase );
  CU_TEST( example );
  CU_TEST( anotherExample ); 
  CU_TEST( testAdd );
  CU_TEST( testDivideByZero );
  CU_TEST( testEquals );
  CU_TEST_SUITE_END();
public:
  ...
    The macro CU_TEST_SUITE actually declares a bunch of typedef, implements the static method suite() which returns the suite for this test case, and start implementing a template method named registerTests.
    The macro CU_TEST actually adds the specified method to the suite, behind the scene, the traditional CppUnit::TestCaller is used.
    The macro CU_TEST_SUITE_END ends the method registerTests that we were defining.

    What do we have at that point:

    All we need now it to register the suite to the test registry (a object that store a collection of test that can be later retrieved. Great to reduce dependency). You can do this by using the following macro in the implementation file of your class (.cpp):

    #include "ExampleTestCase.h"

    CU_TEST_SUITE_REGISTRATION( ExampleTestCase );

    The macro CU_TEST_SUITE_REGISTRATION defines a static variable of type CppUnit::AutoRegisterSuite, with the specified class type. When this variable will be initialized (at static initialization time), it will retrieve the suite() of the class and register it to the TestRegistry. The parameter is the type of the test case, for example, CU_TEST_SUITE_REGISTRATION( ChessTest<Chess> ) is used to register a template test case.

    This great thing about this is that it works just fine with template! No more TestCaller instantiation of the death.

    There is one thing that remaining: how to sub-class a test case ? The macros makes it very easy, instead of using CU_TEST_SUITE to declare the suite, you do as follow:

class SimpleSubTest : public SimpleTest {
  CU_TEST_SUB_SUITE( SimpleSubTest, SimpleTest );
  CU_TEST( testAdd );
  CU_TEST( testSub );
  CU_TEST_SUITE_END();
public:
  ...
    As you can see, you must use the macro CU_TEST_SUB_SUITE and specify the base class as well as the test case class. That's all, it's all done!

The way of templates

    Since your reading this, your are familiar with CppUnit architecture. One of the thing I found frustrating was the building of suite. Here you have your traditional template test case:
#include <cppunit/TestCase.h>
#include <cppunit/TestSuite.h>

template<typename CharType>
class StringTest : public TestCase
{
public:
  static CppUnit::TestSuite *suite();

  void testAppend();
  void testLength();
};

    Here we created a test case for a string class. The test case itself is a template parametrized with the type of character used by the string.

    The static method suite() returns a suite containing the test to run for this test case. Here is the typical way this method is implemented:
 

template<typename CharType>
CppUnit::TestSuite *
StringTest<CharType>::suite()
{
  // Constructs the suite naming it after the test case class name...
  CppUnit::TestSuite *suite = new CppUnit::TestSuite( StringTest<CharType> );

  // adds test caller to the suite for each of the test method.
  suite->addTest( new CppUnit::TestCaller< StringTest<CharType> >(
                      "testAppend",
                      &StringTest<CharType>::testAppend ) );
  suite->addTest( new CppUnit::TestCaller< StringTest<CharType> >(
                      "testLength",
                      &StringTest<CharType>::testLength ) );
}

    I found that very hard to read and maintain, so I created a helper template class to make it easier. This is the TestSuiteBuilder. Here is how to use it:
#include <cppunit/extensions/TestSuiteBuilder.h>

[...]

template<typename CharType>
CppUnit::TestSuite *
StringTest<CharType>::suite()
{
  CppUnit::TestSuiteBuilder< StringTest<CharType> > suite;

  suite.addTestCaller( "testAppend", testAppend );
  suite.addTestCaller( "testLength", testLength );
  return suite.takeSuite();
}

    As you can see it much readable...

    You still have to pass the suite to the test runner. There is two ways to do that:

    You can use the TestFactoryRegistry:

    #include <cppunit/extensions/AutoRegisterSuite.h>

    static CppUnit::AutoRegisterSuite< StringTest<CharType> > suite__;

    This will create a TestSuiteFactory for the specified class and register it to the TestFactoryRegistry.

Using TestRunner from your application

    Now, we still need to run those test case. This is a really simple thing to do. You need to plug a bunch of code in your application. I like to do this in the InitInstance(), after everything is initialized, and with a magic key. In the HostApp example, it is done in CHostAppDoc::OnNewDocument().

    Here is what you need to do if you are using the TestFactoryRegistry (the CU_TEST_SUITE_REGISTRATION macro or the template AutoRegisterSuite):

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

[...]
CHostAppDoc::OnNewDocument()
{
    TestRunner runner;

    runner.addTest ( CppUnit::TestFactoryRegistry::getRegistry().makeTest() );
    // open and run the test runner dialog...
    runner.run();

    [...]
}
    And if you want to do it the traditional way:
#include <TestRunner/TestRunner.h>
#include "StringTest.h"
#include "ExampleTestCase.h"
[...]
CHostAppDoc::OnNewDocument()
{
    TestRunner runner;
    runner.addTest( StringTest<char>::suite() );
    runner.addTest( StringTest<wchar_t>::suite() );
    runner.addTest( ExampleTestCase::suite() );
    runner.run();

    [...]
}

Compiling with CppUnit and TestRunner

    For the includes, there is two ways:     For the library, you need to link CppUnitd.lib and TestRunnerd.lib, which are in the Lib directory. You specify that in Project Settings/Link/General/Library modules. You can you either relative path, or adds the Lib directory in the Tools/Options/Directories tab.
    You must also adds the preprocessor definition USE_TYPEINFO to Project Settings/C++/General/Preprocessor definitions.
    Don't forget to enable the RTTI in Project Settings/C++/C++ Language. They are required to get the test case name (among other things).

    Warning: when running your application, the TestRunnerd.dll which is in the Lib directory need to be the path (see ::LoadLibrary documentation for loading order). I usually ensure that the DLL is in the debug directory by adding a post build "copy" command. See Project Settings/Post-build Step from the HostApp example for detail.

Known bugs

Well, I run into some problem when using the test runner:
Copyright ©2000, Baptiste Lepilleur. http://gaiacrtn.free.fr/index.html