Rosario 3D

My developer blog

Tag Archives: unit test

CppUnit + Hudson = Epic win

Finally I came with a good test driven pipeline for my code. It’s easy to write a test, I don’t have to write a lot of code for the tests and everything is tested automatically.

CppUnit

CppUnit is a test framework inspired by JUnit, writing test in C++ is more difficult then Java cause C++ don’t have language support to navigate trough classes, but again, a good design can solve a lot of programming issue.

With CppUnit all you have to write to run your test is

int main(int argc, char *argv[])
{
  CppUnit::TextUi::TestRunner runner;
  CppUnit::TestResultCollector  collector;
  CppUnit::TestResult result;
  result.addListener(&collector);

  CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
  runner.addTest(registry.makeTest());
  runner.run(result);
 // writing result on a XML file
  std::ofstream xmlFileOut("testresults.xml");
  CppUnit::XmlOutputter xmlOut(&collector, xmlFileOut);
  xmlOut.write();
  return 0;
}

This code create all the structure needed to get all the tests case, collect the results and write everything on a XML file. The file is then analyzed by Hudson.
To create a test case you have to create a class derived by TestFixture and write a methods for each test case. A fixture is a set of test, you can put all your test into one fixture, but is better do subdivide the fixture in different classes for compilation speed and clarity sake. In the test methods you can use some macros to make assertions, if an assertion fail the test fail.
For example here the dot product test to my vector class:

void testDotProduct(){
  CPPUNIT_ASSERT(dot(Vec3f::xAxis, Vec3f::yAxis) == 0.0f);
  CPPUNIT_ASSERT(dot(Vec3f::xAxis, Vec3f::zAxis) == 0.0f);
  CPPUNIT_ASSERT(dot(Vec3f::yAxis, Vec3f::zAxis) == 0.0f);
  CPPUNIT_ASSERT_DOUBLES_EQUAL(dot(op1, op1),
        op1.length()*op1.length(), tollerance);
  CPPUNIT_ASSERT(dot(op1, op2) == dot(op2, op1));  // commutative
  CPPUNIT_ASSERT_DOUBLES_EQUAL(dot(7.0f*op1, 5.0f*op2),
        (7.0f*5.0f)*dot(op1, op2), tollerance);  // distributive
  CPPUNIT_ASSERT(dot(op1, op2+op3) == dot(op1, op2) + dot(op1, op3));
}

To help the test runner to collect (and run) all the test the class should declare a static method called suite, it’s a very boring and repetitive method where you have to list all the test. To make it less boring CppUnit have some macros.

CPPUNIT_TEST_SUITE(VecTest);
   CPPUNIT_TEST(testLinearity);
   CPPUNIT_TEST(testNormalization);
//...
   CPPUNIT_TEST(testCrossProduct);
   CPPUNIT_TEST_EXCEPTION(testDivideByZero, MathError);
   CPPUNIT_TEST(testDotProduct);
CPPUNIT_TEST_SUITE_END();

In the main, you should add all the test suite to the runner, another boring work, also you have to include all the file test in the main. This create a lot of dependence, and you have to recompile the main every time you change a test. To avoid these problems you can register your suite into a test factory using the macro

CPPUNIT_TEST_SUITE_REGISTRATION(VecTest);

, and get the test with the factory.
You can find the whole code in the repository. You can find a guide to learn how to use CppUnit here.

Hudson

Hudson is a wonderful tools to automatically compile your code and make a report in case of errors, and also analyze the CppUnit test report (if you install the appropriate plug-in), that’s why I write a XML file in the test case. It’s designed to work with java but I can run any script to compile (so you can invoke make or any other tool chain you use). It can pool for changes in your repository and check if something is changed. But you can also trigger the build process on commit with a simple hook script in your svn repository. Hudson for every commit will check if something is changed in the project and compile if it’s necessary and will also send you a mail if something is wrong. 🙂

I still have to find a good (easy and free) code coverage analyzer, any suggestion?

Advertisements