'''Index''' [[TableOfContents]] = About this document = This document describes in brief how to install the [http://sourceforge.net/projects/cppunit/ CppUnit] framework under Unix and how to write and run test programs with it. This should get you started with using Cpp``Unit. The document is based on the [http://cppunit.sourceforge.net/doc/lastest/cppunit_cookbook.html CppUnit CookBook]. The examples and many explanations are taken almost verbatim from there, slightly changed and improved for readability. = References = The project's homepage is http://sourceforge.net/projects/cppunit. The latest Doxygen documentation of the project's class hierarchy is http://cppunit.sourceforge.net/doc/lastest/index.html. This may be useful while you read this document. = Installing = Download the latest version of Cpp``Unit from http://sourceforge.net/projects/cppunit. At the time of this writing, it is 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 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 #include 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 ${CXX} -c $< ${CXXFLAGS} ${INCLS} # Implicit rule: How to make an executable from .o-files %: %.o ${CXX} -o $@ $^ ${LIBDIRS} ${LIBS} runComplexNumberTest: runComplexNumberTest.o ComplexNumberTest.o Complex.o runtest: runComplexNumberTest export LD_LIBRARY_PATH=/var/tmp/cppunit/install/lib && ./runComplexNumberTest }}} Note that Cpp``Unit 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 Test``Fixtures, Test``Callers, Test``Suites, and Test``Runners. 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 `TestFixture` is used to provide a common environment for a set of test cases. Each test runs in its own fixture so there can be no side effects among test runs. * 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. `setup()` and `tearDown()` are called ''before'' and ''after'' every test case in the fixture, respectively. {{{ // file ComplexNumberTestFixture.h // A test fixture for Complex, returning a test suite to be run by a runner #include #include #include #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( "testEquality", &ComplexNumberTestFixture::testEquality ) ); suiteOfTests->addTest( new CppUnit::TestCaller( "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 #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 `ComplexNumberTestFixture` using these macros: {{{ // file ComplexNumberTestFixturewithMacro.h // A test fixture for Complex, returning a test suite to be run by a runner #include #include #include #include #include "Complex.h" class ComplexNumberTestFixtureWithMacro : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE( ComplexNumberTestFixtureWithMacro ); CPPUNIT_TEST( testEquality ); CPPUNIT_TEST( testAddition ); CPPUNIT_TEST_SUITE_END(); // remainder as in the class above, but without suite() private: Complex *a, *b, *s; public: void setUp(); void tearDown(); void testEquality(); void testAddition(); }; // of course, the implementation of suite() is also missing in the .cpp file ;-) }}} There are other helper macros, for example, to check that a specific exception is thrown under specific circumstances. Check the documentation. = Test factory registry = It is easy to forget to add a fixture suite to the test runner since it is in another file. Compilation bottleneck may be caused by the inclusion of all test case headers. To solve these problems, Cpp``Unit introduced `TestFactoryRegistry` objects. Such an object is a place where suites can be registered at initialization time. For example, to register the `ComplexNumberTestFixture` suite in the registry, we would add 2 lines in the .cpp file: {{{ // file ComplexNumberTestFixture.cpp #include #include "ComplexNumberTestFixture.h" CPPUNIT_TEST_SUITE_REGISTRATION( ComplexNumberTestFixture ); // now as before... }}} Then, to run the tests using the text test runner, we do not need to include the fixture anymore: {{{ // file runTestSuiteWithTestFactoryRegistry.cpp #include #include int main() { CppUnit::TextUi::TestRunner runner; CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry(); runner.addTest( registry.makeTest() ); runner.run(); } }}} That is, first we retrieve the instance of the `TestFactoryRegistry`. Then we obtain and add a new `TestSuite` created by the `TestFactoryRegistry` that contains all the test suites registered using `CPPUNIT_TEST_SUITE_REGISTRATION()`. You may wonder from where the runner now knows what tests to add: This is specified by simply linking the `ComplexNumberTestFixture.o` object file to the main object file `runTestSuiteWithTestFactoryRegistry.o`. = Automated post-build checks = How do we integrate unit testing to our build process? `TestRunner::run()` returns a boolean indicating wether the run was successful. We can just return this value from `main()`: {{{ #include #include int main() { CppUnit::TextUi::TestRunner runner; CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry(); runner.addTest( registry.makeTest() ); bool wasSuccessful = runner.run( "", false ); return wasSuccessful ? 0 : 1; } }}} Now, we can run our test program after each compilation of our tested application. This can be formulated by an additional command in the makefile. If there is an error (the exit status is nonzero), the `make` utility gives up on the current rule, and perhaps on all rules.