0% found this document useful (0 votes)
121 views

Unit Testing and Code Coverage For C++

The document discusses unit testing C++ code using cppUnit. It describes how to write individual tests using assertion macros, organize tests into test fixtures, suites, and running them with a test runner. It provides an example of testing a Bitmap class, writing tests before code using a test-driven approach. Tests are added incrementally as more functionality is added to the Bitmap class.

Uploaded by

Aiken Chan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
121 views

Unit Testing and Code Coverage For C++

The document discusses unit testing C++ code using cppUnit. It describes how to write individual tests using assertion macros, organize tests into test fixtures, suites, and running them with a test runner. It provides an example of testing a Bitmap class, writing tests before code using a test-driven approach. Tests are added incrementally as more functionality is added to the Bitmap class.

Uploaded by

Aiken Chan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 30

Unit testing and code

coverage for C++

1
cppUnit

Coding an individual test


Use assertion macros defined in unit test library
Elements for building tests
TestFixture, TestSuite, TestRunner

2
Code in an individual test
Use assertion macros defined in cppunit
If assertion fails, an exception is thrown
 CPPUNIT_ASSERT(condition):
 Checks the condition and throws an exception if it's false.
 CPPUNIT_ASSERT_MESSAGE(message, condition):
 Checks the condition and throws an exception and showing specified message if
it is false.
 CPPUNIT_ASSERT_EQUAL(expected,current):
 Checks whether the expected condition is the same as current, and raises an
exception showing the expected and current values.
 CPPUNIT_ASSERT_EQUAL_MESSAGE(message,expected,c
urrent)
 Checks whether the expected is the same as the actual, and raises an exception
showing the expected and current values, and specified message.
 CPPUNIT_ASSERT_DOUBLES_EQUAL(expected,current,delta):
 Checks whether the expected and current difference is smaller than delta. If it 3
fails, the expected and current values are shown.
TestFixture
 Each test should be derived from the abstract
class TestFixture
 Override two functions:
setUp
 operations that would be invoked prior to each individual test,
e.g. create new clean instances of class ready for next test
tearDown
 operations that would be invoked after to each individual test,
e.g. deletes instances

4
TestSuite, TestRunner

TestSuite
Groups a number of tests (using TestFixture) so
that they can all be run
TestRunner
Driver program

5
Test driven development
Test twice, code once
“The style here is to write a few lines of
code, then a test that should run, or even
better, to write a test that won't run, then
write the code that will make it run.”

6
Test driven development – step 0
 Define class Bitmap
Owns an array of unsigned long integers
 Methods
boolean Identity(const Bitmap& other)
 Are we the same Bitmap
 Compare our address with other’s address!
boolean Equals(const Bitmap& other)
 Are we equal?
 What do you mean equal?
• This is the part we test twice – code once!
void Zero()
 Set my bits all to zero.

7
#ifndef __MYBITSCLASS__
#define __MYBITSCLASS__
Class Bitmap
// Code assumes 32-bit unsigned long integers
#define MAXBITS 512
#define NUMWORDS 16
typedef unsigned long Bits;

class Bitmap {
public:
Bitmap();
void Zero(void);
bool Equals(const Bitmap &other) const;
bool Identity(const Bitmap &other) const;
private:
Bits fBits[NUMWORDS];
};
#endif

8
#include "Bitmap.h"
Bitmap::Bitmap() Class Bitmap
{
Zero();
}
void Bitmap::Zero(void)
{
for(int i=0;i<NUMWORDS; i++)
fBits[i] = 0;
}
bool Bitmap::Identity(const Bitmap& other) const
{
return (this == &other);
}
bool Bitmap::Equals(const Bitmap& other) const
{
return Identity(other);
// Incorrect implementation
}

9
Now write a test for Bitmap -- TestFixture
#ifndef BITMAPTEST_H
#define BITMAPTEST_H

#include <cppunit/TestCase.h>
#include <cppunit/extensions/HelperMacros.h>
#include "Bitmap.h"

class BitmapTest : public CppUnit::TestFixture


{
CPPUNIT_TEST_SUITE (BitmapTest);
CPPUNIT_TEST (testEquals);
CPPUNIT_TEST_SUITE_END ();
private:
Bitmap bitmap1;
Bitmap bitmap2;
public:
void setUp();
void tearDown();
protected:
void testEquals();
};

#endif 10
#include "BitmapTest.h"
#include "Bitmap.h"

CPPUNIT_TEST_SUITE_REGISTRATION (BitmapTest);

void BitmapTest::setUp()
{
// Put the bitmaps back in standard state
// (Need at least two as doing things like
// checking equality)
bitmap1.Zero();
bitmap2.Zero();
}

void BitmapTest::tearDown()
{
// Nothing to do
}

11
void BitmapTest::testEquals()
{
// Identity should work
CPPUNIT_ASSERT(bitmap1.Identity(bitmap1));
CPPUNIT_ASSERT(bitmap2.Identity(bitmap2));
CPPUNIT_ASSERT(!bitmap1.Identity(bitmap2));

// At this stage - equality incorrectly defined


// It is identity
// (It shouldn't be!)

// This test should succeed - I'm equal to me


CPPUNIT_ASSERT(bitmap1.Equals(bitmap1));

// so should this
CPPUNIT_ASSERT(bitmap2.Equals(bitmap2));

// But this should fail


CPPUNIT_ASSERT(bitmap1.Equals(bitmap2));
}

12
The Driver -- TestRunner

int main (int argc, char* argv[])


{
// Result collection
CPPUNIT_NS :: TestResult testresult;
CPPUNIT_NS :: TestResultCollector collectedresults;
testresult.addListener (&collectedresults);
CPPUNIT_NS :: TextTestProgressListener tracker;
testresult.addListener(&tracker);

// Setting up runner and running test


CPPUNIT_NS :: TestRunner testrunner;
testrunner.addTest (CPPUNIT_NS :: TestFactoryRegistry :: getRegistry ().makeTest
());
testrunner.run (testresult);

// Reporting
CPPUNIT_NS :: CompilerOutputter compileroutputter (&collectedresults, std::cerr);
compileroutputter.write ();

return collectedresults.wasSuccessful () ? 0 : 1; 13
}
Build and run

!!!FAILURES!!!
Test Results:
Run: 1 Failures: 1 Errors: 0

1) test: BitmapTest::testEquals(F) line: 34


BitmapTest.cc
assertion failed
Expression: bitmap1.Equals(bitmap2)

14
“Refactor”

bool Bitmap::Equals(const Bitmap& other) const


{
for(int i = 0; i < NUMWORDS; i++)
if(this->fBits[i] != other.fBits[i]) return false;
return true;
}

15
Next increment – more functionality in
Bitmap

class Bitmap {
public:
Bitmap();
void Zero(void);
void SetBit(int bitnum);
int Count(void) const;

bool Equals(const Bitmap &other) const;


bool Identity(const Bitmap &other) const;
private:
Bits fBits[NUMWORDS];
};

16
BitmapTest
class BitmapTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE (BitmapTest);
CPPUNIT_TEST (testEquals);
CPPUNIT_TEST (testSetBit);
CPPUNIT_TEST (testSetBitAndCount);
CPPUNIT_TEST_SUITE_END ();
private:
Bitmap bitmap1;
Bitmap bitmap2;
public:
void setUp();
void tearDown();
protected:
void testEquals();
void testSetBit();
void testSetBitAndCount();
};

17
BitmapTest

Extended equality test


Can now make Bitmaps that are not equal (by
using set bit operation)
Add two more extensive tests on new test
and count operations

18
void BitmapTest::testEquals()
{ BitmapTest
// Identity should work
CPPUNIT_ASSERT(bitmap1.Identity(bitmap1));
CPPUNIT_ASSERT(bitmap2.Identity(bitmap2));
CPPUNIT_ASSERT(!bitmap1.Identity(bitmap2));
CPPUNIT_ASSERT(bitmap1.Equals(bitmap1));
CPPUNIT_ASSERT(bitmap2.Equals(bitmap2));
CPPUNIT_ASSERT(bitmap1.Equals(bitmap2));

// Have some functionality to change the bits


// See if equals works after that
// Set a different bit
bitmap1.SetBit(22);
bitmap2.SetBit(123);
// Shouldn't be equals any more!
CPPUNIT_ASSERT(!bitmap1.Equals(bitmap2));
}
19
void BitmapTest::testSetBit()
{ BitmapTest
// If set bit 0 in one, they should not be equal
bitmap1.SetBit(0);
CPPUNIT_ASSERT(!bitmap1.Equals(bitmap2));
// Just being pedantic - reverse roles in test
CPPUNIT_ASSERT(!bitmap2.Equals(bitmap1));
bitmap1.Zero();
// But should be equal after it got cleared
CPPUNIT_ASSERT(bitmap1.Equals(bitmap2));
CPPUNIT_ASSERT(bitmap2.Equals(bitmap1));
// If set a non-existent bit it isn't supposed
// to change anything
bitmap1.SetBit(-1);
bitmap2.SetBit(513);
CPPUNIT_ASSERT(bitmap2.Equals(bitmap1));
}

20
void BitmapTest::testSetBitAndCount()
{ BitmapTest
// Set some bits and check that count equals number of bits set
int bits1[] = { 0, 3, 17, 21, 33, 54, 68, 77, 91, 103, 211, 304 };
int bits2[] = { 0, 31, 32, 33, 63, 64, 65, 300, 400, 500, 511 };
int count1 = sizeof(bits1)/sizeof(int);
int count2 = sizeof(bits2)/sizeof(int);
for(int i=0;i<count1;i++)
bitmap1.SetBit(bits1[i]);
for(int i=0;i<count2;i++)
bitmap2.SetBit(bits2[i]);
CPPUNIT_ASSERT_EQUAL(count1, bitmap1.Count());
// Deliberate error to get a failed test
CPPUNIT_ASSERT_EQUAL(count1, bitmap2.Count());
}

21
Rebuild, rerun

!!!FAILURES!!! Text
Test Results:
Run: 3 Failures: 1 Errors: 0

1) test: BitmapTest::testSetBitAndCount (F) line: 77


BitmapTest.cc
equality assertion failed
- Expected: 12
- Actual : 11
22
cppunit

Available at
http://sourceforge.net/projects/cppunit

Is installed on Linux machines in lab used


for CSCI222

23
Tutorials …

http://www.comp.nus.edu.sg/~cs3214s/too
ls/cppunitSol.html
http://www.evocomp.de/tutorials/tutorium_
cppunit/howto_tutorial_cppunit_en.html
Documentation with cppUnit
Docs folder in /share/cs-pub/222/testing
“Money” tutorial

24
Code coverage
g++ compiler and
gcov analysis tool

25
Compilation for code coverage

Set extra compile flags


In NetBeans setup, it is simplest just to specify
--coverage
in the "command line" options for both the compilation
and linker steps

26
Run, then use gcov to analyze
 NetBeans can be used to run the application
under test
Best to run several times with different input data so as
to try to test all options
 gcov analysis should be done in terminal
session
cd to directory with C++ source
Identify directory where gcov files (.gcno, .gcda) were
created – should be ./build/Debug/GNU-Linux-x86
Run gcov specifying the data directory and file for
analysis

27
Gcov analysis : function summaries
gcov –o ./build/Debug/GNU-Linux-x86 -f Bitmap.cc
100.00% of 3 source lines executed in function void Bitmap::Zero()
100.00% of 7 source lines executed in function void Bitmap::SetBit(int)
100.00% of 8 source lines executed in function void Bitmap::ClearBit(int)
0.00% of 4 source lines executed in function void Bitmap::SetAs(int, int)
85.71% of 7 source lines executed in function int Bitmap::TestBit(int) const
100.00% of 7 source lines executed in function void Bitmap::FlipBit(int)
100.00% of 2 source lines executed in function void
Bitmap::ReadFrom(std::fstream&)

100.00% of 4 source lines executed in function int Bitmap::Equals(const
Bitmap&) const
94.32% of 88 source lines executed in file Bitmap.cc

28
Gcov analysis: branch counts
$ gcov –o ./build/Debug/GNU-Linux-x86 -c Bitmap.cc

94.32% of 88 source lines executed in file Bitmap.cc
Creating Bitmap.cc.gcov.
$ cat Bitmap.cc.gcov
#include "Bitmap.h"
Bitmap::Bitmap()
14 {
14 Zero();
}
void Bitmap::Zero(void)
15 {
255 for(int i=0;i<NUMWORDS; i++)
240 fBits[i] = 0;
}
void Bitmap::SetBit(int bitnum)
113 {
113 if((bitnum < 0) || (bitnum >= MAXBITS))
113 return;
113 int word = bitnum / 32;

29
Inspect profiles

Profiles and function summaries


immediately reveal untested code.

30

You might also like