The idea behind CppUnit is to develop parallel classes that will be the ones in charge of testing our 'real' classes. But let's have a look with an example.
1. Installing CppUnit
The first step of course is to get the tool and install it. You can download the last version from sourceforge (at this time 1.12.1) :
http://sourceforge.net/projects/cppunit/files/cppunit/
Untar the downloaded file, and from a terminal move inside the resultant directory and execute the following commands:
./configure
make
sudo make install
2. Using CppUnit for testing our Shared Libraries
A. Creating a shared library to be tested
Now that CppUnit is installed in our system it is time to use it. As example, I will test a shared library that under Eclipse. You can find how to create shared libraries on this previous post:
http://linuxtortures.blogspot.fr/2012/03/shared-libraries-with-eclipse-on-8664.html
Basically open eclipse and click on File --> new, and under the opened dialog select Shared Library, and Linux GCC
As we are under a 64 bits architecture, do not forget to compile with -fPIC, otherwise you will get the following error:
/usr/bin/ld: ./Calculator.o: relocation R_X86_64_32S against `vtable for Calculator' can not be used when making a shared object; recompile with -fPIC
You can activate -fPIC compilation under eclipse by right click on your project and under properties check the following option:
Our library will contain a class 'Calculator' that implements two methods: add and sub (please note that both method are inlined for the shake of simplicity):
A. Creating the testing project
Now let's create our test project, that it will be just an an executable linked to our shared library and of course to the CppUnit framework.
First create a new executable project:
Now link your project against libdl, libcppunit and of course the result of the shared library that we are planning to test. You will need to include the folder where the shared library is generated on the library search path:
The libdl is needed in order to avoid errors of the kind:
/usr/local/lib/libcppunit.so: undefined reference to `dlsym'
/usr/local/lib/libcppunit.so: undefined reference to `dlopen'
/usr/local/lib/libcppunit.so: undefined reference to `dlclose'
/usr/local/lib/libcppunit.so: undefined reference to `dlopen'
/usr/local/lib/libcppunit.so: undefined reference to `dlclose'
You will also need to include the path to our shared library headers in order to use it:
B. Creating the testing class:
Once finished ((almost)) all the including and configuring steps let's focus now on the 'pure' testing procedure.
The most basic case is to just create a class that inherit from CppUnit::TestCase and perform all the needed 'ASSERTS' on the overridden method 'runTest()', then on the main method of our testing project we just need to create a 'CppUnit::TextUi::TestRunner' and include our just created class as a test to be run:
Here below you can find the code for our test class:
And here the one corresponding to the main method (the one actually in charge of of execute the tests):
That's all, the last step you have to perform before running your test project is just to include your shared library on the environment of the run configuration:
Now you can run your testing project an have a look to the results on the console:
2. Making things more sophisticated by using TestFixture and TestSuite
What we have explained until now is quite nice and it should allows you to start writing your unit test within a cpp environment, but let's complicate the things a bit more and let's talk about testsuites and fixtures.
As you can see on the previous example, we are using the class TestCalculator and its overridden method runTest to create a test. This approach could be enough for most of the cases, but in some cases trying to test the functionality of a library by writing all the asserts in one method or by creating a lot of classes (one per test) can lead to confusion.
Here is where the TestFixture and TestSuite suites arrive. A TestFixture, basically allows us to convert methods of a class into a testcase, and a TestSuite will allows us to group all those tests and execute them as if it were just one. But let's see all that with an example:
A. Modifying TestCalculator
Now our TestCalculator.h class is going to look like that:
#ifndef TESTCALCULATOR_H_
#define TESTCALCULATOR_H_
#include <Calculator.h>
#include <cppunit/TestFixture.h>
#include <cppunit/TestAssert.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestSuite.h>
class TestCalculator: public CppUnit::TestFixture {
public:
TestCalculator();
virtual ~TestCalculator();
static CppUnit::Test *suite()
{
CppUnit::TestSuite *suiteOfTests = new CppUnit::TestSuite( "CalculatorTestSuite" );
suiteOfTests->addTest( new CppUnit::TestCaller<TestCalculator>(
"Test Sum",
&TestCalculator::testSum ) );
suiteOfTests->addTest( new CppUnit::TestCaller<TestCalculator>(
"Test Subtraction",
&TestCalculator::testSub ) );
return suiteOfTests;
}
void setUp()
{
}
void tearDown()
{
}
private:
Calculator calculatorToBeTested;
protected:
void testSum()
{
CPPUNIT_ASSERT(calculatorToBeTested.add(1,2)==3);
}
void testSub()
{
CPPUNIT_ASSERT(calculatorToBeTested.sub(5,1)==4);
}
};
#endif /* TESTCALCULATOR_H_ */
As you can appreciate there is a static method returning a class Test that indeed returns a TestSuite. This is can be easily understood by having a look to the following diagram:
Indeed it is a composite pattern (Design Pattern) so basically a TestSuite has the same type than TestCase and on the top of everything can indeed contains TestCases.
On the other hand, test caller also inherit from TestCase so with this statemen:
suiteOfTests->addTest( new CppUnit::TestCaller<TestCalculator>(
"Test Sum",
&TestCalculator::testSum ) );
We area adding TestCases to our TestSuite on the form of a methods (testSum and testSub). It is also worth to mention that setUp method is called each time before executing the actual testMethod and tearDown right after executing it, allowing us to set some pre-conditions (in case of needed) release them.
The last modification we have to perform to have everything working is on our main method:
#include <iostream>
#include <cppunit/TestSuite.h>
#include <cppunit/ui/text/TestRunner.h>
#include "TestCalculator.h"
using namespace std;
int main() {
CppUnit::TextUi::TestRunner runner;
runner.addTest(TestCalculator::suite());
runner.run();
return 0;
}
We are now adding the suite returned from the static method to our runner. Here the results:
Note that now we are running two tests. In case we deliberately make one test fail:
void testSum()
{
CPPUNIT_ASSERT(calculatorToBeTested.add(1,2)==4);
}
Here the result, showing clearly the name of the test that failed, the line, and the assert.
0 comments:
Post a Comment