Yolinux.com Tutorial

CppUnit: C++ unit test framework

C++ class unit test framework examples and integration techniques for Jenkins.

Tutorial Table of Contents:

Related YoLinux Tutorials:

°Linux and C++

°C++ Unions & Structures

°C++ Templates

°C++ STL

°C++ STL Map

°C++ String Class

°C++ Singleton

°C++ Coding Style

°C++ XML Beans

°C/C++ Dynamic Memory

°C++ Memory Corruption

°C/C++ Signal Handling

°Jenkins CI

°YoLinux Tutorials Index




Free Information Technology Magazines and Document Downloads
TradePub link image


CppUnit Description:

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

Man page:
  • assert - abort the program if assertion is false


Installation:

Available as Linux RPMs:
  • cppunit-1.12.0-3.el5.rf
  • cppunit-devel-1.12.0-3.el5.rf
Docs installed to: /usr/share/doc/doc/cppunit-devel-1.12.0/index.html

Debian/Ubuntu: apt-get install libcppunit-doc libcppunit-dev

CppUnit Home Page


CppUnit Example:

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;
}
          
Note the use of XmlOutputter for XML output of results
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>
          

Integration with Jenkins:

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:
Jenkins/Hudson JUnit configuration
Configuration

For more, see the YoLinux.com Jenkins tutorial


Books:

C++ How to Program
by Harvey M. Deitel, Paul J. Deitel
ISBN #0131857576, Prentice Hall

Fifth edition. The first edition of this book (and Professor Sheely at UTA) taught me to program C++. It is complete and covers all the nuances of the C++ language. It also has good code examples. Good for both learning and reference.

Amazon.com
Exceptional C++: 47 Engineering Puzzles, Programming Problems and Solutions
by Herb Sutter
ISBN #0201615622, Addison-Wesley Professional

Advanced C++ features and STL.

Amazon.com
More Exceptional C++
by Herb Sutter
ISBN #020170434X, Addison-Wesley Professional

Amazon.com
Effective C++: 50 Specific Ways to Improve Your Programs and Design (2nd Edition)
by Scott Meyers
ISBN #0201924889, Addison-Wesley Professional

Amazon.com
More Effective C++: 35 New Ways to improve your Programs and Designs
by Scott Meyers
ISBN #020163371X, Addison-Wesley Professional

Amazon.com

   

    Bookmark and Share


Advertisements




Copyright © 2012 by Greg Ippolito