Skip to content

Getting started with CppUnit

“Puzzle” by Olga Berrios (CC BY 2.0)

Recently I had the oportunity to start implementing unit tests in a C++ project. The chosen testing framework was CppUnit, which is an port for C++ of Java’s JUnit. To be honest, CppUnit is not as popular as it used to be, specially compared with its more handsome and stilysh cousin JUnit (at least JUnit seems to have a website written after 2005).

The main issue is that documentation comes from fairly old articles, and it is not as accesible and up-to-date as with other projects. Taking into account that modern alternatives exists, such as googletest, one could be tempted to run away and never look back. However, let’s not be afraid and try to come up with a working example of CppUnit.

Installation

Installation was straightforward, you simply have to download the source code and let autotools do its magic. What was difficult was finding the most recent version of the software. It seems that Freedesktop’s version of CppUnit is the one that is being more actively maintained, so it was our winner.

git clone git://anongit.freedesktop.org/git/libreoffice/cppunit/
cd cppunit
./autogen.sh
./configure
make
sudo make install

After the installation is finished, we have to reload the register of installed libraries:

sudo ldconfig

We can check that it has worked by using the -p flag:

ldconfig -p | grep -i cppunit

Writting the example

Now we can start to implement our “Hello World” CppUnit test. I have tried to create an example simple but realistic, similar to what you could find in a real project. Consequently, the test is implemented in thre different files:

  • Header (example1_simpleAssertions.h): in this file we define which tests are going to be part of the test suite (group of closely related tests). Test suites normally refer to a single software module. In addition, set-up and clean-up tasks (executed before and after the text, respectively) are defined in the header.
  • Source (example1_simpleAssertions.cpp): here implement the body of the test, what they are actually about. This is were assertions (CPPUNIT_ASSERT_)
  • Test runner (run_tests.cpp): it contains the main process which launches the test suit.

The source code is included in the following sections, and it can also be obtain from my Github page. They contain comments providing an explanation for what each part of the code does.

Header

#ifndef MY_EXAMPLE_MODULE_TESTS
#define MY_EXAMPLE_MODULE_TESTS

#include <string>

#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>

// This class will contain the declaration of the unit tests. It inherits from
// the CppUnit:TestFixture class. "TestFixture" is being used instead of the
// simpler "TestCase" because "TextFixture" allows the user to define a set
// of objects that will be used throughout the test suite, so that they do not
// have to be declared on every time. The definition of the objects takes place
// in the setUp() method, and they are destroyed in the tearDown() method. Such
// methods are not present in the "TestUnit" class
class MyExampleModuleTests : public CppUnit::TestFixture
{
    // Start the declaration of a new test suite
	CPPUNIT_TEST_SUITE(MyExampleModuleTests);

    // Groups of tests to be run. We use the term 'group' because although
    // each CPPUNIT_TEST declares a single CppUnit test, each of them will
    // be divided into smaller tests (implemented as independent methods),
    // which will be run one after the other
	CPPUNIT_TEST(exampleTests);
	CPPUNIT_TEST(moreExampleTests);

    // End the declaration of the test suite
	CPPUNIT_TEST_SUITE_END();

	public:

    // The setUp process if for initializing fixtures and preparing everything
    // that we need before test execution
	void setUp()
	{
		std::cout << "Setting up" << std::endl;
        number1 = 1;
	}

    // The teardown process is for performing clean-up after the test. If we
    // had allocated memory during setUp, this would be the place for freeing
    // it
	void tearDown()
	{
		std::cout << "Finished" << std::endl;
	}

    // These methods will be used to call the test() method of each individual
    // test group
	void exampleTests();
	void moreExampleTests();

    protected:
        int number1;
        int number2;
};

#endif /* MY_EXAMPLE_MODULE_TESTS */

Source

#include "example1.h"

// For a list of existing assertion types, check: http://cppunit.sourceforge.net/doc/cvs/group___assertions.html
//

// Our first group of tests. In this one, two types of assertions are shown,
// testAssert and testAssertNegated. As it can be seen, one after the other in
// the test() method of the class.
class exampleTests{
    public:
        static bool test() {
            testAssert();
            testAssertNegated();
            return true;
        }
    private:
        static bool testAssert() {
            // Assertions that a condition is true
            CPPUNIT_ASSERT(1 == 1);
            return true;
        }
        static bool testAssertNegated() {
            // Check that 2 is NOT smaller than 1
            CPPUNIT_ASSERT(! 2 < 1);
            return true;
        }
};

// Our second group of tests, this time with two additional types of assertions
class moreExampleTests{
    public:
        static bool test() {
            testAssertEqual();
            testAssertAssertionFail();
            return true;
        }
    private:
        static bool testAssertEqual() {
            // Assert that two numbers are equal
            CPPUNIT_ASSERT_EQUAL(5,5);
            return true;
        }
        static bool testAssertAssertionFail() {
            // Assert that saying "5 is bigger than 8" is a false
            CPPUNIT_ASSERT_ASSERTION_FAIL(5 > 8);
            return true;
        }
};

void MyExampleModuleTests::exampleTests(void) {
    exampleTests::test();
}
void MyExampleModuleTests::moreExampleTests(void) {
    moreExampleTests::test();
}

Test runner

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

#include "example1_simpleAssertions.h"

// For a test suite to be run when executing runner.run(), it must first be
// registered
CPPUNIT_TEST_SUITE_REGISTRATION( MyExampleModuleTests);

int main( int argc, char **argv)
{
    // Object in charge of executing the registered test suites
	CppUnit::TextUi::TestRunner runner;

	// We start by getting the test registry (TestFactoryRegistry)
	CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();

	// When we call makeTest() we create a test suite object for the test
    // that has been previously registered with _REGISTRATION
    // Then we use the result to add it to the list of tests suites to be
    // executed by runner.run()
	runner.addTest(registry.makeTest());
    bool success = runner.run("", false);

    // We can use the return value of the main function to perform further
    // actions. For example, if the unit tests are being launched in the
    // context of a continuous integration system such as Jenkins, this could
    // tell Jenkins that it should abort the build process because the unit
    // tests for one module have failed
    return success ? 0 : 1;
}

Run tests

Now we can compile the code. Do not forget to link with -lcppunit.

g++ run_tests.cpp example1_simpleAssertions.cpp -o run_tests -lcppunit

When we run the executable, we obtain the following output:

[master][~/programming/cpp/cppunit/example1]$ ./run_tests
.Setting up
Finished
.Setting up
Finished

OK (2 tests)

So it works! Now it’s a matter of writing more and more complex tests to cover all of our application’s functionality. A good place to start is Group Assertions, which provides a list of available assertion types. Their name and the brief description provided pretty much explain what can they be used for.

For example, if we change the expression being asserted so that it checks for failed assertions:

CPPUNIT_ASSERT_ASSERTION_FAIL(1 == 1);

and run the tests again, we see one failure, since obviously 1 is equal to 1, so the assertion doest NOT fail (wich is what we wanted with our new code, so the test is considered as failed).

[master][~/programming/cpp/cppunit/example1]$ ./run_tests
.Setting up
Finished
.Setting up
Finished

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

test: MyExampleModuleTests::exampleTests (F) line: 17 example1.cpp
assertion failed
- Expression: ! 1 == 1

Final remarks

This post has covered some basic aspects of CppUnit. Of course, there are many more features to be analyzed. As I get more familiar with this framework, I will try to add some more advanced examples which are more likely to be applicable in real-world situations.

References

Final remarks

Source code presented in this post can be obtained from Github page.

Published inTesting

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *