Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Template test cases

In order to test a template based component, it is frequently necessary to perform the same set of checks for a component instantiated with different template parameters.

One way to perform the same set of checks for a component instantiated with different template parameters would be:

template <typename T>
void single_test()
{
  BOOST_CHECK( /* test assertion */ );
}

void combined_test()
{
  single_test<int>();
  single_test<float>();
  single_test<unsigned char>();
}

There several problems/inconveniences with above approach, including:

The Unit Test Framework provides a facility, the template test case, to create a series of test cases based on a list of desired types and nullary function. This facility comes with an automatic and manual registration interface.

[Tip] Tip

The test case template facility is preferable to the approach in example above, since execution of each sub test case is guarded and counted separately. It produces a better test log/results report (in example above in case of failure you can't say which type is at fault) and allows you to test all types even if one of them causes termination of the sub test case.

Template test case with automated registration

A template test case, registered automatically and in place of its implementation, is declared through the macro BOOST_AUTO_TEST_CASE_TEMPLATE:

BOOST_AUTO_TEST_CASE_TEMPLATE(test_case_name, formal_type_parameter_name, collection_of_types);

The arguments are as follow:

  1. test_case_name: the test case template name: unique test cases template identifier
  2. formal_type_parameter_name: the name of a formal template parameter: name of the type the test case template is instantiated with
  3. collection_of_types: the collection of types to instantiate test case template with. This is an arbitrary MPL sequence or a sequence of types wrapped in a std::tuple (since Unit Test Framework v3.7, if supported by the compiler)

The resulting name of the test is a composition of the test_case_name parameter and the current type being tested. Since Unit Test Framework v3.12, the framework tries to unify the name of the resulting type across various platforms such that they are easier to reference from the command line filter.

Example: Test case template with automated registration

Code

#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <boost/mpl/list.hpp>

typedef boost::mpl::list<int,long,unsigned char> test_types;

BOOST_AUTO_TEST_CASE_TEMPLATE( my_test, T, test_types )
{
  BOOST_TEST( sizeof(T) == (unsigned)4 );
}

typedef std::tuple<int, long, unsigned char> test_types_w_tuples;

BOOST_AUTO_TEST_CASE_TEMPLATE( my_tuple_test, T, test_types_w_tuples )
{
  BOOST_TEST( sizeof(T) == (unsigned)4 );
}

Output

> example
Running 3 test cases...
test.cpp(17): error: in "my_test<unsigned char>": check sizeof(T) == (unsigned)4 has failed [1 != 4]

*** 1 failure is detected in the test module "example"
[Warning] Warning

Since Unit Test Framework v3.7, the Unit Test Framework does not allow for duplicate test case name under the same test suite. As test names are derived from the types in the collection_of_types, this indirectly means that having a duplicate type in the collection_of_types yields an error.

[Note] Note

If you prefer having the template parameter list directly in the declaration of BOOST_AUTO_TEST_CASE_TEMPLATE, you may use the macro BOOST_IDENTITY_TYPE. The previous example gives (note the double parenthesis around the MPL list):

#include <boost/utility/identity_type.hpp>

BOOST_AUTO_TEST_CASE_TEMPLATE(
  my_test,
  T,
  BOOST_IDENTITY_TYPE((boost::mpl::list<
    int,
    long,
    unsigned char
  >)) )
{
  BOOST_TEST( sizeof(T) == (unsigned)4 );
}
Test case template with manual registration

To manually register template test cases, two macros should be used:

The macro BOOST_TEST_CASE_TEMPLATE_FUNCTION requires two arguments:

  1. the name of the test case template and
  2. the name of the format type parameter
BOOST_TEST_CASE_TEMPLATE_FUNCTION(test_case_name, type_name);
BOOST_TEST_CASE_TEMPLATE_FUNCTION( test_case_name, type_name )
{
  // test case template body
}

The macro BOOST_TEST_CASE_TEMPLATE_FUNCTION is intended to be used in place of nullary function template signature:

template <typename type_name>
void test_case_name()
{
  // test case template body
}

The only difference is that the BOOST_TEST_CASE_TEMPLATE_FUNCTION makes the test case template name usable in the template argument list.

BOOST_TEST_CASE_TEMPLATE requires two arguments:

  1. the name of the test case template and
  2. Boost.MPL compatible collection of types to instantiate it with.

The names passed to both macros should be the same.

BOOST_TEST_CASE_TEMPLATE(test_case_name, collection_of_types);
Example: Manually registered test case template

Code

#include <boost/test/included/unit_test.hpp>
#include <boost/mpl/list.hpp>
using namespace boost::unit_test;

BOOST_TEST_CASE_TEMPLATE_FUNCTION( my_test, T )
{
  BOOST_TEST( sizeof(T) == 4U );
}

test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
{
  typedef boost::mpl::list<int,long,unsigned char> test_types;

  framework::master_test_suite().
    add( BOOST_TEST_CASE_TEMPLATE( my_test, test_types ) );

  return 0;
}

Output

> example
Running 3 test cases...
test.cpp(15): error: in "my_test<unsigned char>": check sizeof(T) == 4U has failed [1 != 4]

*** 1 failure is detected in the test module "Master Test Suite"

BOOST_TEST_CASE_TEMPLATE creates an instance of the test case generator. When passed to the method test_suite::add, the generator produces a separate sub test case for each type in the supplied collection of types and registers it immediately in the test suite. Each test case is based on the test case template body instantiated with a particular test type.

The names for the sub test cases are deduced from the macro argument test_case_name. If you prefer to assign different test case names, you need to use the underlying make_test_case interface instead. Both test cases creation and registration is performed in the test module initialization function.

[Warning] Warning

Since Unit Test Framework v3.7, the Unit Test Framework does not allow for duplicate test case name under the same test suite. As test names are derived from the types in the collection_of_types, this indirectly means that having a duplicate of types in the collection_of_types will yield an error.


PrevUpHomeNext