Boost Unit Test Framework

In my new job I am back to using C++ again. One of the first things that drew my attention was the C++ unit test framework that "we" use. In my opinion that framework has some drawbacks - non-disclosure here :( - and I decided to look for an alternative. Some Googling taught me that the Boost Unit Test Framework (UTF) is a well-regarded one so I decided to look into that, also as the Boost framework is already used here.

My initial impressions of the UTF are positive. Tests are really easy to write as they require next to no boilerplate code. As always with Boost, the library itself is well-maintained and well-documented. And in case the docs do not provide you with an answer, that answer is only a search query away.

Although the UTF has some extensive documentation, it still took me some time to get my sample code running. It turned out to be really easy in the end and I have published my code samples at Bitbucket in repo cmake-boost-test. The remainder of this post contains a slightly modified version of the README of that repo, at least as it was at the moment of writing this post.

Usage variants

As described in the UTF documentation in the section on Usage variants, there are several ways you can use the UTF in your code: by including a single header, by linking against a static library or by linking against a dynamic library. This repo contains examples of these variants of using the UTF. They are built and tested using Boost 1.49.0, CMake 2.8.7, and Ubuntu 12.04.

Code

Header file tests.h contains two tests:

BOOST_AUTO_TEST_CASE(TestThatSucceeds)
{
  BOOST_CHECK(true);
}

BOOST_AUTO_TEST_CASE(TestThatFails)
{
  BOOST_CHECK(false);
}

Usually I would not implement tests in a header file but in this way I can demonstrate the different variants without having to duplicate the test code.

Binary my-boost-test is built from main.cpp, which includes the single header variant of unit_test.hpp:

#define BOOST_TEST_MAIN

#include <boost/test/included/unit_test.hpp>

#include "tests.h"

When you compile this into an executable, a main function is automatically created . This function collects all the tests in a test suite called "Master Test Suite", executes them and writes the results to stdout. That is all there is to it.

When you define BOOST_TEST_MODULE instead of BOOST_TEST_MAIN, the main test suite is named after your define of BOOST_TEST_MODULE. For example,

#define BOOST_TEST_MODULE  "This is my test suite."

will create a main test suite that is called "This is my test suite". This also holds for the other variants.

There is a limitation with the single header variant: all the tests have to be implemented in a single file. As the example shows you can work around this if you implement the tests in header files but that is not the way header files should be used. If you want to implement the tests in separate files, you have to link against the static or dynamic Boost unit test library.

Binary my-static-boost-test is linked against the static Boost unit test library. It is built from main-static.cpp:

#define BOOST_TEST_MAIN

#include <boost/test/unit_test.hpp>

and from tests.cpp:

#include <boost/test/unit_test.hpp>

#include "tests.h"

This variant differs in two other ways from the single header variant:

  1. the header file to include is different;
  2. the executable has to be linked against libboost_unit_test_framework.a.

Note that you have to define BOOST_TEST_MAIN once before the inclusion of unit_test.hpp. If you do not do this, the test suite initialization function, which is required, is not automatically created. If you do this more than once, the initialization function will be defined multiple times, which will break the build.

Binary my-dynamic-boost-test links against the dynamic Boost unit test library and is built from main-dynamic.cpp:

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN

#include <boost/test/unit_test.hpp>

and the aforementioned tests.cpp. This variant differs in two ways from the static library variant:

  1. BOOST_TEST_DYN_LINK needs to be defined before the inclusion of the header file;
  2. the executable has to be linked against libboost_unit_test_framework.so.

Final notes

When you use the dynamic variant, if you do not define BOOST_TEST_MAIN or BOOST_TEST_MODULE, you can (and have to) define your own test runner. This variant is called the external test runner variant. I have not looked into this (yet).

Finally, the CMake listfile specifies the build of two binaries with the word static in them: my-static-boost-test and my-static-name-boost-test. These binaries differ in the way the listfile specifies where the Boost unit test library is located:

  • for my-static-boost-test, it specifies the full path to the library;
  • for my-static-name-boost-test, it specifies the name of the library.

This is a side effect of the fact that I used this code to familiarize myself with CMake.

Comments

Comments powered by Disqus