•  
  •  

Parameterizing CppUnit tests

There are several C++ unit testing frameworks. The most used one is CppUnit. Unfortunately it doesn’t provide the functionality like its big brother JUnit does. Such a feature is the support of parameterized unit tests. Developing a parameterized test with JUnit is quite easy. A unit test class just needs to be annotated with @RunWith(Parameterized.class). Additionally a method providing the test data sets as Collection<Object[]> has to be annotated with @Parameters. A more detailed example can be found here.

C++ lacks of some useful features, e.g. Annotations. Although this is no reason to not support parameterized tests the CppUnit team didn’t implement support for it yet. This article shows how to implement parameterized tests anyway providing almost the same comfort as JUnit does.

Parameterizing a unit test means that a single method is called providing one of a set of parameters. Such tests are useful when the implementation of tests only differ in the parameters they process.

brainchild GmbH has developed some enhancements for CppUnit. Those are some macros used to define test suites. These macros rely on two methods the test implementation must provide:

static std::vector parameters();
void testWithParameter(ParameterType& parameter);

CPPUNIT_PARAMETERIZED_TEST_SUITE

CPPUNIT_PARAMETERIZED_TEST_SUITE ( TestFixtureType, ParameterType )

This macro extends CPPUNIT_TEST_SUITE with the parameter ParameterType. The ParameterType specifies the type of the parameter.

Example:

CPPUNIT_PARAMETERIZED_TEST_SUITE ( MyTest, std::string )

CPPUNIT_PARAMETERIZED_TEST_SUITE_END

CPPUNIT_PARAMETERIZED_TEST_SUITE_END

This macro was introduced just for consistency reason. It is the same as CPPUNIT_TEST_SUITE_END.


CPPUNIT_PARAMETERIZED_TEST_SUITE_REGSITRATION

CPPUNIT_PARAMETERIZED_TEST_SUITE_REGISTRATION ( TestFixtureType, ParameterType )

This macro extends CPPUNIT_TEST_SUITE_REGISTRATION with the parameter ParameterType. It implements the test parameterization logic.

Example:

CPPUNIT_PARAMETERIZED_TEST_SUITE_REGISTRATION ( MyTest, std::string )

Example:

ParameterizedTest.hpp

class ParameterizedTest: public CPPUNIT_NS::TestFixture {
public:

ParameterizedTest();
~ParameterizedTest();
void setUp();
void tearDown();

/**
* Retrieves the test parameters. Each entry is passed as a single parameter * to a test.
*
* @return the parameters list.
*/
static std::vector parameters();

/**
* This method is called with a single test parameter.
*
* @param parameter The parameter to be used for the test.
*/
void testWithParameter(std::string parameter);

void testOrdinaryTest();

private:

CPPUNIT_PARAMETERIZED_TEST_SUITE(ParameterizedTest, std::string);

CPPUNIT_TEST(testOrdinaryTest);

CPPUNIT_PARAMETERIZED_TEST_SUITE_END();
};

ParameterizedTest.cpp

CPPUNIT_PARAMETERIZED_TEST_SUITE_REGISTRATION(ParameterizedTest, std::string);

ParameterizedTest::ParameterizedTest() {
// empty
}

ParameterizedTest::~ParameterizedTest() {
// empty
}

void ParameterizedTest::setUp(){
// empty
}

void ParameterizedTest::tearDown(){
// empty
}

static std::string buildParameter(size_t testNumber){ std::ostringstream result;
result << "Parameter " << testNumber;
return result.str();
}

std::vector ParameterizedTest::parameters(){
std::vector result; for(size_t i = 0; i < 3; i++){
result.push_back(buildParameter(i)); return result;
}
}

void ParameterizedTest::testWithParameter(std::string param){

CPPUNIT_ASSERT_EQUAL(buildParameter(m_currentTestNumber), param);
}

/**
* This test should be called after all parameterized tests.
*/
void ParameterizedTest::testOrdinaryTest(){

CPPUNIT_ASSERT_EQUAL(m_parameters.size(), m_currentTestNumber);
}

The unit test class defines two test methods: testWithParameter and testOrdinaryTest. testWithParameter is called using a Parameter of type std::string. They are provided by the method parameters. testOrdinaryTest shows that ordinary tests can occurr in parallel to parameterized ones. This test verifies that the current test counter equals the number of parameters.

Note:
The above macros define infernal variables an methods, e.g. m_currentTestNumber. this variable starts counting from 1 and is incremented after each parametereized test execution. It provide the number of the current test.


Here is the entire file:

Parameterized.hpp


#ifndef PARAMETERIZED_HPP_

#define PARAMETERIZED_HPP_

#include <cppunit/extensions/HelperMacros.h>

/**

* Extends the macro CPPUNIT_TEST_SUITE in order to easily specify a parameterized

* unit test.It expects the following methods to be specified by the implementing

* unit test:

*</pre>
&nbsp;
<ul>
<ul>*
	<li><code>static std::vector parameters();</code></li>
</ul>
</ul>
&nbsp;
<ul>
<ul>*
	<li><code>void testWithParameter(ParameterType</code></li>
</ul>
</ul>
&nbsp;
<ul>*</ul>
<pre>
*

* @param ATestFixtureType Type of the test case class.

* @param ParameterType the type of a single parameter.

*

* \see CPPUNIT_TEST_SUITE

*/

#define CPPUNIT_PARAMETERIZED_TEST_SUITE( TestFixtureType, ParameterType ) \

void __test(); \

\

static std::vector m_parameters; \

static size_t m_currentTestNumber; \

\

CPPUNIT_TEST_SUITE(TestFixtureType); \

\

m_parameters = parameters(); \

m_currentTestNumber = 0; \

\

for (size_t i = 0; i < m_parameters.size(); i++) { \

std::ostringstream testName; \

testName << "testWithParameter: " << i; \

\

CPPUNIT_TEST_SUITE_ADD_TEST( \

( new CPPUNIT_NS::TestCaller( \

context.getTestNameFor( testName.str() ), \

&TestFixtureType::__test, \

context.makeFixture() ) )); \

}

/**

* Used to declare the end of a parameterized test suite.

*/

#define CPPUNIT_PARAMETERIZED_TEST_SUITE_END CPPUNIT_TEST_SUITE_END

/**

* Extends the macro CPPUNIT_TEST_SUITE_REGISTRATION in order to provide

* necessary framework code in implementation file. This code actually implements

* the parameterization behavior.

*/

#define CPPUNIT_PARAMETERIZED_TEST_SUITE_REGISTRATION( TestFixtureType, ParameterType ) \

CPPUNIT_TEST_SUITE_REGISTRATION(TestFixtureType); \

\

std::vector TestFixtureType::m_parameters; \

size_t TestFixtureType::m_currentTestNumber; \

\

void TestFixtureType::__test(){ \

\

testWithParameter(m_parameters.at(m_currentTestNumber++)); \

}
#endif /* PARAMETERIZED_HPP_ */