Java程序辅导

C C++ Java Python Processing编程在线培训 程序编写 软件开发 视频讲解

客服在线QQ:2653320439 微信:ittutor Email:itutor@qq.com
wx: cjtutor
QQ: 2653320439
3/04/2014
1
Unit testing and code 
coverage for C++
cppUnit and gcov
1
This lecture segment
cppUnit
Reference material
TDD
Partial development of Bitmap class in the “Test 
Driven Development” style
TDD ideas illustrated
cppUnit code illustrated
Code coverage example
2
cppunit
Available at
http://sourceforge.net/projects/cppunit
 Is installed on Linux machines in lab used 
for CSCI222
3
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
4
cppUnit
Reference
5
cppUnit reference
Coding an individual test
Use assertion macros defined in unit test library
Elements for building tests
TestFixture, TestSuite, TestRunner
Output formatters
Compilation and linking notes
makefile etc
6
3/04/2014
2
Test classes - Money
Money – example in cppUnit
(yeeech – looks more like Java than C++. Reason? It’s based on Beck’s own 
Money example which is in Java)
class Money { 
public: 
Money( double amount, std::string currency ) : 
m_amount( amount ) , m_currency( currency ) { } 
double getAmount() const { return m_amount; }
std::string getCurrency() const
{ return m_currency; } 
private: 
double m_amount; 
std::string m_currency; 
}; 
7
Test classes – Fraction (A. Müller) –
the "EvoComp" Web reference
class Fraction
{
public:
// constructor
Fraction (int = 0, int = 1) throw (DivisionByZeroException);
// copy-constructor and assignment-operator
Fraction (const Fraction&);
Fraction& operator= (const Fraction&);
// comparing operators
bool operator== (const Fraction&) const;
bool operator!= (const Fraction&) const;
// arithmetic operators
friend Fraction operator+ (const Fraction&, const Fraction&);
friend Fraction operator- (const Fraction&, const Fraction&);
// output on stdout
friend ostream& operator<< (ostream&, const Fraction&);
private:
// method for reducing
void reduce (void);
// variables for saving the numerator and denominator
int numerator, denominator;
};
8
Code in an individual test
Use assertion macros defined in cppunit
 (Macros are used because compiler can add in source file line number)
 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,curr
ent):
 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 
fails, the expected and current values are shown.
9
Assertion macro expands …
CPPUNIT_NS::Asserter::failIf( !(condition), \
CPPUNIT_NS::Message( "assertion failed", \ "Expression: " #condition), \ CPPUNIT_SOURCELINE() ) ) 
 Inserts into code a call to (static) method in Asserter class
static public void CPPUNIT_API fail (const Message &message, const 
SourceLine &sourceLine=SourceLine())
Throws a Exception with the specified message and location.
#define CPPUNIT_SOURCELINE() CPPUNIT_NS::SourceLine( __FILE__, 
__LINE__ )
 Constructs a SourceLine object initialized with the location where the macro is 
expanded.
 __FILE__, __LINE__ are compiler variables updated as code is compiled
10
Assertion examples
void MoneyTest::testEqual() 
{ 
// Set up 
const Money money123FF( 123, "FF" ); 
const Money money123USD( 123, "USD" );
const Money money12FF( 12, "FF" ); 
const Money money12USD( 12, "USD" ); 
// Process & Check 
CPPUNIT_ASSERT( money123FF == money123FF ); 
CPPUNIT_ASSERT( money12FF != money123FF );
…
11
Assertion examples
void MoneyTest::testAdd() 
{ // Set up 
const Money money12FF( 12, "FF" ); 
const Money expectedMoney( 135, "FF" ); 
// Process 
Money money( 123, "FF" ); 
money += money12FF;
// Check 
CPPUNIT_ASSERT( expectedMoney == money ); // add
// add should return ref. on 'this'.
CPPUNIT_ASSERT( &money == &(money += money12FF) );
} 
12
3/04/2014
3
Assertion examples …
void fractiontest :: setUp (void)
{
// set up test environment (initializing objects)
a = new Fraction (1, 2);
b = new Fraction (2, 3);
c = new Fraction (2, 6);
d = new Fraction (-5, 2);
e = new Fraction (5, -2);
f = new Fraction (-5, -2);
g = new Fraction (5, 2);
h = new Fraction ();
}
13
Assertion examples …
void fractiontest :: addTest (void)
{
// check subtraction results
CPPUNIT_ASSERT_EQUAL (*a + *b, Fraction (7, 6));
CPPUNIT_ASSERT_EQUAL (*b + *c, Fraction (1));
CPPUNIT_ASSERT_EQUAL (*d + *e, Fraction (-5));
…
14
Assertion examples …
 void fractiontest :: equalTest (void)
{
// test successful, if true is returned
CPPUNIT_ASSERT (*d == *e);
CPPUNIT_ASSERT (Fraction (1) == Fraction (2, 2));
CPPUNIT_ASSERT (Fraction (1) != Fraction (1, 2));
// both must have equal valued
CPPUNIT_ASSERT_EQUAL (*f, *g);
CPPUNIT_ASSERT_EQUAL (*h, Fraction (0));
…
15
Assertion examples …
 void fractiontest :: exceptionTest (void)
{
// an exception has to be thrown here
CPPUNIT_ASSERT_THROW (Fraction (1, 0),
DivisionByZeroException);
}
16
Elements for building tests
TestFixture, TestSuite, 
TestRunner
17
TestFixture
 Introduced to express idea of 
 setUp
 tearDown
operations that would be invoked prior to and after each 
individual test.
Keep the individual tests independent
 Typically, TestFixture “manages resources”
 Defines some pointers to instances of class under test
 setUp does a series of new operations to create new clean 
instances of class ready for next test
 tearDown deletes instances
 Can work with instance members that are instance of 
class under test provided the class defines a member 
function that restores instance to standard state
18
3/04/2014
4
TestSuite, TestRunner
TestSuite
Groups a number of tests (using TestFixture) so 
that they can all be run
cppUnit C++ code needed to define a TestSuite 
is ugly
HelperMacros make it clean!
TestRunner
Driver program
19
Helper macros
#define CPPUNIT_TEST_SUITE(ATestFixtureType)
Begin test suite. 
#define CPPUNIT_TEST_SUITE_END()
End declaration of the test suite. 
#define CPPUNIT_TEST(testMethod)
Add a method to the suite. 
#define CPPUNIT_TEST_EXCEPTION(testMethod, ExceptionType)
Add a test which fail if the specified exception is not caught. 
#define CPPUNIT_TEST_FAIL(testMethod) CPPUNIT_TEST_EXCEPTION( 
testMethod, CPPUNIT_NS::Exception )
Adds a test case which is excepted to fail. 
#define CPPUNIT_TEST_SUITE_REGISTRATION(ATestFixtureType)
#define CPPUNIT_REGISTRY_ADD(which, to)
#define CPPUNIT_REGISTRY_ADD_TO_DEFAULT(which)
There are additional less commonly used macros
20
Example TestFixtures and suites
class fractiontest : public CPPUNIT_NS :: TestFixture
{
CPPUNIT_TEST_SUITE (fractiontest);
CPPUNIT_TEST (addTest);
CPPUNIT_TEST (subTest);
CPPUNIT_TEST (exceptionTest);
CPPUNIT_TEST (equalTest);
CPPUNIT_TEST_SUITE_END ();
public:
void setUp (void);
void tearDown (void);
protected:
void addTest (void);
void subTest (void);
void exceptionTest (void);
void equalTest (void);
private:
Fraction *a, *b, *c, *d, *e, *f, *g, *h;
};
Tests
Compose tests
into “suite”
“Resources”
managed by fixture21
fractiontest
#include "fractiontest.h "
CPPUNIT_TEST_SUITE_REGISTRATION (fractiontest);
void fractiontest :: setUp (void)
{
// set up test environment (initializing objects)
a = new Fraction (1, 2);
b = new Fraction (2, 3);
…
}
void fractiontest :: tearDown (void) 
{
// finally delete objects
delete a; delete b; delete c; delete d;
…
}
Test suite registration macro defines some variables with
all actual TestSuite etc instantiated (at link load time) 22
Money
class MoneyTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE( MoneyTest );
CPPUNIT_TEST( testConstructor ); 
CPPUNIT_TEST_SUITE_END(); 
public: 
void setUp(); 
void tearDown(); 
void testConstructor(); 
}; 
23
Money
#include "MoneyTest.h" 
// Registers the fixture into the 'registry' 
CPPUNIT_TEST_SUITE_REGISTRATION( 
MoneyTest ); 
void MoneyTest::setUp() { } 
void MoneyTest::tearDown() { } 
void MoneyTest::testConstructor() { 
CPPUNIT_FAIL( "not implemented" ); 
} 
“the macro CPPUNIT_TEST_SUITE_REGISTRATION() is used to create a static
variable that automatically registers its test suite in a global registry “ 24
3/04/2014
5
Helper macros – template classes
 The test suite macros can even be used with 
templated test classes. For example: 
template class StringTest : 
public CppUnit::TestFixture 
{ 
CPPUNIT_TEST_SUITE( StringTest ); 
CPPUNIT_TEST( testAppend ); 
CPPUNIT_TEST_SUITE_END(); 
public: ... 
}; 
25
TestRunner and driver program
Basics are simple
Create a TestRunner
Link it to some “reporting” component
Link it to test suite
Run the tests
Lots of options
Mainly relating to different output formats
26
Money
int main(int argc, char* argv[]) 
{ 
// Get the top level suite from the registry 
CppUnit::Test *suite = 
CppUnit::TestFactoryRegistry::getRegistry().makeTest(); 
// Adds the test to the list of test to run 
CppUnit::TextUi::TestRunner runner; runner.addTest( suite ); 
// Change the default outputter to a compiler error format outputter 
runner.setOutputter( new CppUnit::CompilerOutputter( 
&runner.result(), std::cerr ) ); 
// Run the tests.
bool wasSucessful = runner.run(); 
// Return error code 1 if the one of test failed. 
return wasSucessful ? 0 : 1; 
} 
27
Fraction
int main (int argc, char* argv[])
{
CPPUNIT_NS :: TestResult testresult;
CPPUNIT_NS :: TestResultCollector collectedresults;
testresult.addListener (&collectedresults);
CPPUNIT_NS :: TestRunner testrunner;
testrunner.addTest (CPPUNIT_NS :: TestFactoryRegistry :: getRegistry 
().makeTest ());
testrunner.run (testresult);
CPPUNIT_NS :: CompilerOutputter compileroutputter (&collectedresults, 
std::cerr);
compileroutputter.write ();
return collectedresults.wasSuccessful () ? 0 : 1;
}
28
Outputters …
CompilerOutputter 
Outputs a TestResultCollector in a compiler 
compatible format. 
Printing the test results in a compiler compatible 
format (assertion location has the same format 
as compiler error), allow you to use your IDE to 
jump to the assertion failure.
TextOutputter
XmlOutputter
Save the test result as a XML stream.  
29
Compilation
Need to include appropriate headers
TestFixture class
#include 
#include 
Testdriver program
#include 
#include 
#include 
#include 
#include 
30
3/04/2014
6
Now where are those header files …
 #include “Money.h”
 Your stuff, current directory
 #include 
 Standard C and C++ headers
 Somewhere on your system in a directory called include
 Your compiler knows where
 What about  etd?
 Cygwin treats cppunit as core
 It adds the header-libraries to the main C/C++ include collection
 Others? 
 Typical install creates /usr/local/include/cppunit
 But these include files are not processed automatically 
 You must modify your “include path” 
31
And where are the run-time libraries …
This was covered in the lecture introducing 
the NetBeans IDE
32
Makefile ? ! …
Leave it to NetBeans
You simply add the reference to 
/usr/include/cppunit for the compiler
And you add the reference to /usr/lib/cppunit.a 
for the linker
Use "wizard" dialogs to add these references
33
Test driven development
34
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.”
OK,
I’ll play your game (but just this once)
I’ll develop “Bitmap” in TDD style
35
TestFixture, suite, and driver 
Driver program
 Based on A Müller’s test program for his 
fraction class
Doesn’t get changed during development of 
Bitmap class and its tests
BitmapTest, Bitmap
These classes evolve as we expand Bitmap 
functionality
BitmapTest is a TestFixture, helper macros 
used to create TestSuite using it
36
3/04/2014
7
Driver
#include 
#include 
#include 
#include 
#include 
#include 
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);
37
Driver - contd
…
// 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;
}
38
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!
 (Think we can get this right)
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.
39
TestFixture
Owns couple of instances of Bitmap
setUp
Clean them out using Zero
 tearDown
Nothing to do
Test identity
Test equality
40
#ifndef BITMAPTEST_H
#define BITMAPTEST_H
#include 
#include 
#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
41
#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
}
42
3/04/2014
8
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));
}
43
Class Bitmap
#ifndef __MYBITSCLASS__
#define __MYBITSCLASS__
// 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
44
Class Bitmap
#include "Bitmap.h"
Bitmap::Bitmap()
{
Zero();
}
void Bitmap::Zero(void)
{
for(int i=0;i



BitmapTest::testEquals
Assertion

BitmapTest.cc
34

assertion failed
- Expression: bitmap1.Equals(bitmap2)





1
1
0
1

 46
“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;
}
47
GUI interfaces
Possible using Qt C++ graphics package
48
3/04/2014
9
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];
};
49
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();
};
50
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
51
BitmapTestvoid BitmapTest::testEquals(){
// 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));
}
52
BitmapTestvoid BitmapTest::testSetBit(){
// 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));
}
53
BitmapTestvoid BitmapTest::testSetBitAndCount(){
// 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



BitmapTest::testSetBitAndCount
Assertion

BitmapTest.cc
77

equality assertion failed
- Expected: 12
- Actual  : 11





BitmapTest::testEquals


BitmapTest::testSetBit



3
1
0
1


Compiler
57
Code coverage
g++ compiler and
gcov analysis tool
58
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
59
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
60
3/04/2014
11
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
61
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= MAXBITS))
113                    return;
113            int word = bitnum / 32;
…
62
Inspect profiles
Profiles and function summaries 
immediately reveal untested code.
63