JOIN
Get Time
features   
Discuss this article
The art of testing C++ code: An introduction to cppUnit

By sql_lall
TopCoder Member

Background:
Before a motor company releases a new car, it must first be tested to make sure the steering, acceleration and brakes function correctly. Before releasing a new medical drug into the market, it undergoes strict testing to ensure it does what it claims with no undesired harm. In the same way, before any software is released into the wild, it should first be subjected to rigorous tests to try to confirm that each section of the code is bug free.

Unit testing, popularized as part of the framework for Xtreme Programming, is one possible method for code-testing based on testing every aspect of the code for well-defined behavior. To assist developers, unit testing code has been developed for many different platforms, commonly referred to as xUnit. TCS developers may recognize many listed there, such as jUnit, jsUnit, nUnit, and httpUnit (covered in this feature). cppUnit is the port of this framework for use with C++ projects.

Building your unit:
Before developing cppUnit code, it must be first built locally. Detailed information is available from the cppUnit wiki build page, and is summarized below. This process applies to those on a Unix machine or running cygwin under Windows:
  1. Obtain the source, available from cppUnit's sourceforge download page. At the time of writing, the most recent stable version is 1.10.2
  2. Unpack locally by unzipping and untarring the download.
  3. Check configuration details available by reading the INSTALL-x file provided. Beware that some information provided about cygwin here is outdated and incorrect.
  4. Generate makefiles by running ./configure.
  5. Compile it all by calling make. This will output a lot of information to the console, and may include warnings, but hopefully no errors.
  6. Test the installation with make check. Yes, you are testing testing code!
  7. Install libraries and headers with make install.
For those using Linux or cygwin, it may be useful to check first to see if cppUnit has already been installed. A good indication is to see if ./cppunit-config, a cppUnit utility, can run.

If you are interested in using cppUnit from Windows using Visual Studio, there will be a .dsw file provided that allows you to test a project in the Post-Build phase. For more information, see the build page linked above. Also available are instructions to build cppUnit within Eclipse.

Unit interaction:
Unit testing with cppUnit is based around TestFixture instances, each of which contains a suite of test cases.

To write some tests of your own, first create a test class that extends publicly from the CppUnit::TestFixture class. Inside that you can then create as many void test methods as you like, performing various assertion tests using the multitude of helpful test macros provided by TestAsserts.h. For example, here is a method which tests the return value of a method multiple times, using most of the available macros:
class suiteOfTests : public CppUnit::TestFixture {
    /* snip */
public:
    void ageTest(){
        int age = ClassImTesting.getAge();

        // simple asserts
        CPPUNIT_ASSERT(age == 18);
        CPPUNIT_ASSERT_MESSAGE("Must be 18", age == 18);

        // asserting equality:
        CPPUNIT_ASSERT_EQUAL(age, 18);
        CPPUNIT_ASSERT_EQUAL_MESSAGE("Must be 18", age, 18);
        CPPUNIT_ASSERT_DOUBLES_EQUAL(age * 1.0, 18.0, 1e-10);

        // exception asserts:
        CPPUNIT_ASSERT_THROW( ClassImTesting->testAge(age - 1), WrongAgeException);
        CPPUNIT_ASSERT_NO_THROW(  ClassImTesting->testAge(age), WrongAgeException);

        // inverse asserts:
        if(age != 18)
            CPPUNIT_FAIL("Must be 18");
        CPPUNIT_ASSERT_ASSERTION_FAIL( CPP_UNIT_ASSERT( age != 18 ));
    }

    void setUp(){...}    // used to set up the require preconditions
    void tearDown(){...} // called after tests are run, in order to clean up.
};
    
As you can see, the macros provided give a variety of ways to test the code, including checking for boolean true/false, equality with optional epsilon, checking error handling, and manually failing or inverting an assertion. Each macro expands into a cppUnit assertion which includes a message (which you may provide in xx_MESSAGE macros) to help you find the test if it fails, as well as the line in the source from which it originates.

There are also two functions, setUp and tearDown, where you should put any logic that must be executed before or after the tests are run, respectively.

In addition it is simple to create your own assertions, including custom macros. Their creation is beyond the scope of this introduction, but a clear example is available here.


Declaration of intent:
The benefit to using a TestFixture comes when telling the rest of your code where your tests lie. Again, through the use of macros, cppUnit will automatically put all of your tests together in a testing suite, available statically. Use is self-explanatory, for example:
#include <cppunit/TestCase.h>
#include <cppunit/extensions/HelperMacros.h>

class suiteOfTests : public CppUnit::TestFixture {
    // generation macros, using the class and method names
    CPPUNIT_TEST_SUITE( suiteOfTests ); // use CPPUNIT_TEST_SUB_SUITE for subclasses,
                                        //    to call inherited tests
        CPPUNIT_TEST( ageTest );        // add each method name in a CPPUNIT_TEST call
        CPPUNIT_TEST( heightTest );     //        "                        "

    CPPUNIT_TEST_SUITE_END();           // generates the TestSuite* suite() method

    public:
    void ageTest(){...}
    void heightTest(){...}
    /* snip */
};
    
The result of these will be an additional two methods:
static void addTestsToSuite(...), which will be called by a tester to register all the methods found inside a CPPUNIT_TEST macro.
static CPPUNIT_NS::TestSuite *suite(), which returns a suite of tests that can be executed at runtime.

Final preparation:
  • Testing manually: Having written all these test cases, no doubt you'll be wanting to execute them at some stage. cppUnit provides a TestRunner class that you can add TestSuites to, as well as execute by calling its run(...) method.
  • Testing using registry: It may not suprise you to learn that there is a more automated way of running tests, through use of macros found inside HelperMacros.h. As shown below, you can register your fixtures by putting their class name inside a CPPUNIT_TEST_SUITE_REGISTRATION macro. This will add the entire suite to a Factory registry, which can be obtained statically later and added to a TestRunner (see below).
  • XML generation: The default output for cppUnit is to print to the console the suite results, as well as any errors encountered. As with other xUnit ports, cppUnit provides a simple way to also output all results to an XML file. It is constructed as in instance of XmlOutputter as shown below, and added to the TestRunner before the suites are run.
    Other forms of output are supported, such as GUI using MFC or Qt depending on compile environment. It is also possible to create your own custom output formatters, feel free to explore the cppUnit wiki's information about this.
#include <fstream>
#include <cppunit/TestCase.h>
#include <cppunit/XmlOutputter.h>
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/extensions/TestFactoryRegistry.h>

    // ...
    CPPUNIT_TEST_SUITE_REGISTRATION( suiteOfTests );

    // ...
    CppUnit::TestRunner runner; // creates a test executor

    // Specify XML output and inform the test runner of this format (optional)
    CppUnit::XmlOutputter* outputter =
            new CppUnit::XmlOutputter(&runner.result(), std::ofstream("result.xml"));
    runner.setOutputter(outputter);

    // use the suite factory, then execute all tests
    runner.addTest( CppUnit::TestFactoryRegistry::getRegistry().makeTest() );
    runner.run( "", false );
All units go:
Once you have written your cppUnit code, the following should compile it using gcc, as long as the initial build step was successful. This assumes the main() method is in cppTester.cpp, and you want the executable to be called textExec (though the names are easily changed):
g++ $(cflags) -o testExec cppTester.cpp ..[any other .cpp / .o].. $(lflags)
where $(cflags) are the required compiler flags, as found by running ./cppunit-config --cflags
and $(lflags) are the required librarys to be linked, found with ./cppunit-config --link

Debriefing:
Hopefully this introduction has introduced enough of cppUnit for you to start writing and running your own suite of tests.
However, cppUnit has many more features and options for you to explore if you wish. Some places to start include: In addition, if you have any questions regarding cppUnit or this article in particular, feel free to ask at the forums.