The CppUnit test framework is for unit test of C++ class functions. It relies on the hierarchy of a test suite comprising of unit test cases which test class functions. The test begins with setUp() followed by the test and ending with tearDown().
Each unit test employs the use of C++ assert() to test the function results.
C++ assert prototype: void assert (int expression);
If this expression evaluates to 0, this causes an assertion failure that terminates the program.
The assert function will abort the application if false.
Turn assert off: #define NDEBUG
at the beginning of its code, before the inclusion of assert.h
Also see the YoLinux Tutorial on Google C++ unit test.
Man page:- assert - abort the program if assertion is false
Available as Linux RPMs:
- cppunit-1.12.0-3.el5.rf
- cppunit-devel-1.12.0-3.el5.rf
Debian/Ubuntu: apt-get install libcppunit-doc libcppunit-dev
C++ class to unit test:
File: CBasicMath.hpp
#ifndef BASIC_MATH_HPP__ #define BASIC_MATH_HPP__ class CBasicMath { public: int Addition(int x, int y); int Multiply(int x, int y); }; #endif
File: CBasicMath.cpp
#include "CBasicMath.hpp" int CBasicMath::Addition(int x, int y) { return (x + y); } int CBasicMath::Multiply(int x, int y) { return (x * y); }
CppUnit test driver program:
File: TestBasicMath.cpp#include <iostream> #include <string> #include <list> #include <cppunit/TestCase.h> #include <cppunit/TestFixture.h> #include <cppunit/ui/text/TextTestRunner.h> #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> #include <cppunit/TestResult.h> #include <cppunit/TestResultCollector.h> #include <cppunit/TestRunner.h> #include <cppunit/BriefTestProgressListener.h> #include <cppunit/CompilerOutputter.h> #include <cppunit/XmlOutputter.h> #include <netinet/in.h> #include "CBasicMath.hpp" using namespace CppUnit; using namespace std; //----------------------------------------------------------------------------- class TestBasicMath : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestBasicMath); CPPUNIT_TEST(testAddition); CPPUNIT_TEST(testMultiply); CPPUNIT_TEST_SUITE_END(); public: void setUp(void); void tearDown(void); protected: void testAddition(void); void testMultiply(void); private: CBasicMath *mTestObj; }; //----------------------------------------------------------------------------- void TestBasicMath::testAddition(void) { CPPUNIT_ASSERT(5 == mTestObj->Addition(2,3)); } void TestBasicMath::testMultiply(void) { CPPUNIT_ASSERT(6 == mTestObj->Multiply(2,3)); } void TestBasicMath::setUp(void) { mTestObj = new CBasicMath(); } void TestBasicMath::tearDown(void) { delete mTestObj; } //----------------------------------------------------------------------------- CPPUNIT_TEST_SUITE_REGISTRATION( TestBasicMath ); int main(int argc, char* argv[]) { // informs test-listener about testresults CPPUNIT_NS::TestResult testresult; // register listener for collecting the test-results CPPUNIT_NS::TestResultCollector collectedresults; testresult.addListener (&collectedresults); // register listener for per-test progress output CPPUNIT_NS::BriefTestProgressListener progress; testresult.addListener (&progress); // insert test-suite at test-runner by registry CPPUNIT_NS::TestRunner testrunner; testrunner.addTest (CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest ()); testrunner.run(testresult); // output results in compiler-format CPPUNIT_NS::CompilerOutputter compileroutputter(&collectedresults, std::cerr); compileroutputter.write (); // Output XML for Jenkins CPPunit plugin ofstream xmlFileOut("cppTestBasicMathResults.xml"); XmlOutputter xmlOut(&collectedresults, xmlFileOut); xmlOut.write(); // return 0 if tests were successful return collectedresults.wasSuccessful() ? 0 : 1; }
Compile: g++ -o testBasicMath CBasicMath.cpp TestBasicMath.cpp -lcppunit
File: Makefile
CXX = g++ INCLUDES= -I./ CXXFLAGS = -g $(INCLUDES) SRCM= ../CBasicMath.cpp OBJM = $(SRCM:.cpp=.o) LINKFLAGS= -lcppunit testbasicmath: TestBasicMath.cpp $(OBJM) $(CXX) $(CXXFLAGS) -o $@ TestBasicMath.cpp $(OBJM) $(LINKFLAGS) $(LINKFLAGSLOG4) $(LIBLOG) # Default compile .cpp.o: $(CXX) $(CXXFLAGS) -c $< -o $@
Run test: ./testBasicMath
TestBasicMath::testAddition : OK TestBasicMath::testMultiply : OK
This generates the XML file: cppTestBasicMathResults.xml
<?xml version="1.0" encoding='ISO-8859-1' standalone='yes' ?> <TestRun> <FailedTests></FailedTests> <SuccessfulTests> <Test id="1"> <Name>TestBasicMath::testAddition</Name> </Test> <Test id="2"> <Name>TestBasicMath::testMultiply</Name> </Test> </SuccessfulTests> <Statistics> <Tests>2</Tests> <FailuresTotal>0</FailuresTotal> <Errors>0</Errors> <Failures>0</Failures> </Statistics> </TestRun>
Note that I had no luck with the CppUnit plugin for Jenkins and integration required explicit translation of XML unit test results to JUnit format for use by Jenkins.
Ant script to execute Makefile:
File: build.xml
<property name="make.cmd" value="/usr/bin/make"/> ... ... ... <target name="testBasicMathCompile" description="cppunit test compile" > <exec dir="${build.native}/Test" executable="${make.cmd}" failonerror="true"> <arg value="testbasicmath"/> </exec> </target> <target name="testbasicmath" description="cppunit test run" > <exec dir="${build.native}/Test" executable="./testbasicmath" failonerror="false"> </exec> </target>
The XML results of CppUnit require translation to JUnit style XML for interpretation by Jenkins.
Requires XML conversion from CPP Unit to JUnit using XSLT: cpp2junit.xslt
File: cpp2junit.xslt
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"> <testsuite> <xsl:attribute name="errors"><xsl:value-of select="TestRun/Statistics/Errors"/></xsl:attribute> <xsl:attribute name="failures"> <xsl:value-of select="TestRun/Statistics/Failures"/> </xsl:attribute> <xsl:attribute name="tests"> <xsl:value-of select="TestRun/Statistics/Tests"/> </xsl:attribute> <xsl:attribute name="name">from cppunit</xsl:attribute> <xsl:apply-templates/> </testsuite> </xsl:template> <xsl:template match="/TestRun/SuccessfulTests/Test"> <testcase> <xsl:attribute name="classname" ><xsl:value-of select="substring-before(Name, '::')"/></xsl:attribute> <xsl:attribute name="name"><xsl:value-of select="substring-after(Name, '::')"/></xsl:attribute> </testcase> </xsl:template> <xsl:template match="/TestRun/FailedTests/FailedTest"> <testcase> <xsl:attribute name="classname" ><xsl:value-of select="substring-before(Name, '::')"/></xsl:attribute> <xsl:attribute name="name"><xsl:value-of select="substring-after(Name, '::')"/></xsl:attribute> <error> <xsl:attribute name="message"> <xsl:value-of select=" normalize-space(Message)"/> </xsl:attribute> <xsl:attribute name="type"> <xsl:value-of select="FailureType"/> </xsl:attribute> <xsl:value-of select="Message"/> File:<xsl:value-of select="Location/File"/> Line:<xsl:value-of select="Location/Line"/> </error> </testcase> </xsl:template> <xsl:template match="text()|@*"/> </xsl:stylesheet>
Use the Gnome program "xsltproc" to apply XSLT to the CppUnit output XML.
Command: xsltproc -o junitTestBasicMathResults.xml cpp2junit.xslt cppTestBasicMathResults.xml
Translated results: junitTestBasicMathResults.xml
<?xml version="1.0"?> <testsuite errors="0" failures="0" tests="2" name="from cppunit"> <testcase classname="TestBasicMath" name="testAddition"/> <testcase classname="TestBasicMath" name="testMultiply"/> </testsuite>
Ant script for results translation:
File: build.xml
<property name="xsltproc.cmd" value="/usr/bin/xsltproc"/> ... ... ... <target name="testdbCpp2junit" description="Convert cppunit test results to junit test results" > <echo>Tests run from directory: ${build.native}/Test</echo> <exec dir="${build.native}/Test" executable="${xsltproc.cmd}" failonerror="true"> <arg value="-o"/> <arg value="test/data/junitTestBasicMathResults.xml"/> <arg value="cpp2junit.xslt"/> <arg value="cppTestBasicMathResults.xml"/> </exec> </target>
xsltproc man page - command line XSLT processor
Jenkins configuration to display JUnit results:
Configuration
For more, see the YoLinux.com Jenkins tutorial