I’ve just started a new programming project — a library for dense HMMs that uses parallel hardware for its computations, if you want to know — and I decided to use Boost.Test for my unit testing.
Normally, I just write my unit tests with asserts and maybe a few home-made macros, but since I am going to use Boost heavily in the code anyway (I do more and more these days) I figured I might as well try out its unit testing framework.
Problems with the documentation
To my great surprise, I had some problems with the documentation of the framework.
Usually, the documentation for the boost libraries is excellent — at least compared to most libraries I use — and if you just read the documentation for Boost.Test it looks great.
There is a lot of it, with detailed descriptions of this and that and with tutorials to get you started.
It’s just that the examples there do not work.
Take for example this program from the tutorial:
#define BOOST_TEST_MODULE MyTest
#include <boost/test/unit_test.hpp>
int add( int i, int j ) { return i+j; }
BOOST_AUTO_TEST_CASE( my_test )
{
// seven ways to detect and report the same error:
BOOST_CHECK( add( 2,2 ) == 4 ); // #1 continues on error
BOOST_REQUIRE( add( 2,2 ) == 4 ); // #2 throws on error
if( add( 2,2 ) != 4 )
BOOST_ERROR( "Ouch..." ); // #3 continues on error
if( add( 2,2 ) != 4 )
BOOST_FAIL( "Ouch..." ); // #4 throws on error
if( add( 2,2 ) != 4 ) throw "Ouch..."; // #5 throws on error
BOOST_CHECK_MESSAGE( add( 2,2 ) == 4, // #6 continues on error
"add(..) result: " << add( 2,2 ) );
BOOST_CHECK_EQUAL( add( 2,2 ), 4 ); // #7 continues on error
}
The BOOST_AUTO_TEST_CASE() macro should create a test function and plug it into the framework, and after compiling the file (and linking with -lboost_unit_test_framework) you should have a test program.
Well, you can compile the program, but you cannot link it. There is no main() function.
Oh well, if you read the header file boost/test/unit_test.hpp you find these lines:
#if defined(BOOST_TEST_DYN_LINK) && defined(BOOST_TEST_MAIN) && !defined(BOOST_TEST_NO_MAIN)
int BOOST_TEST_CALL_DECL
main( int argc, char* argv[] )
{
return ::boost::unit_test::unit_test_main( &init_unit_test, argc, argv );
}
so it seems that the framework will define main, if only you have defined the right symbols, and yes, adding
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
to the top of the program makes it run.
There were a few other cases like this, where I couldn’t figure out how to use the framework. Like testing template functions with a list of different template parameters, but I just worked my way around that problem with my own macros.
Using the framework
There’s a lot of different things you can do with Boost.Test, but so far I’ve just used the very basic functionality.
I use the BOOST_AUTO_TEST_CASE() macro for my test functions. The different cases are automatically grouped into test suites — one per file (compilation unit) — so I don’t worry about the larger framework. I write a few test cases per code unit I need to test and the rely on the default behaviour of the framework.
The actual testing is done through various BOOST_CHECK_* macros like in the program above.
Among the macros are tests for floating point numbers that lets you test that two numbers are equal up to a certain accuracy. This is what you want to check, since testing equality of floating point numbers is rarely a good idea.
So far I’m happy with Boost.Test, and I’m going to try out some of the more advanced features as my project progresses, I think.