Unit Testing With CppUnit

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 18

Unit Testing with

JUnit and CppUnit

Software Testing
Fundamentals (1)
z What is software testing?
z The process of operating a system or component under
specified conditions, observing or recording the results,
and making an evaluation of some aspect of system or
component. (IEEE Definition)
z Why do we test?
z To ensure the quality and satisfaction of the product
z To gain the confidence in the correctness of the product
z Testing vs. debugging
z Testing is to show that a program has bugs
z Debugging is to locate and correct the error or
misconception that cause the program failures
2
Software Testing
Fundamentals (2)
z Software Testing Techniques
z White-box testing
z The tester has access to the details of the
program under test and performs the testing
according to such details.
z Examples: path testing, branch testing
z Black-box testing
z Black-box testing treats the program under test as
a “black box.” No knowledge about the
implementation is assumed.
z Examples: equivalent partitioning, boundary value
analysis
3

Software Testing
Fundamentals (3)
z Testing should begin “in the small” and progress
toward testing “in the large”
z Unit testing
z Concentrates on each unit (i.e., component) of the software
z Integrated testing
z Building a system from its components and testing the
resultant system for detecting component interaction
problems
z System testing
z Concern with testing an increment to be delivered or entire
system
z Acceptance testing
z Performed by the clients/users to confirm that the product
meets the business requirements
4
Unit Testing
z Unit Testing
z Is normally considered as an adjunct to the coding step
z Focuses verification effort on the smallest unit of software
design – the software component or module
z Using the component-level design description as a guide
z Provide a release criterion for a programming task
z Unit Testing in the OO Context
z Smallest testable unit is the encapsulated class
z Conducting class testing (i.e., unit testing)
z Methods within the class are tested
z The state behavior of the class is examined

Unit Testing Example:


Statement coverage (1)
class X { Function under test
. . .
void f() {
. . .
. . . Test case(s)
} class XTest {
}; . . .
void testf() {
X x;
x.f();
Statement coverage: CPPUNIT_ASSERT(…);
are all statements }
executed? . . .
};
6
Unit Testing Example:
Statement coverage (2)
class X {
. . .
void f(bool x) {
. . .
if (x) {
. . . class XTest {
} . . .
void testf() {
. . .
X x;
} x.f(false);
}; CPPUNIT_ASSERT(…);
There are unexecuted
}
statements
. . .
};
7

Unit Testing Example:


Statement coverage (3)
class X {
. . .
void f(bool x) {
. . .
if (x) {
. . . class XTest {
} . . .
void testf() {
. . .
X x;
} x.f(true);
}; CPPUNIT_ASSERT(…);
All statements
}
are executed
. . .
};
8
Unit Testing Example: Branch
coverage (1)
class X {
. . .
void f(bool x) {
. . .
if (x) {
Unexercised . . . class XTest {
} . . . All statements
void testf() { are executed
. . .
X x;
} x.f(true);
}; CPPUNIT_ASSERT(…);
}
Brach coverage:
. . .
are all control
};
transfer exercised?
9

Unit Testing Example: Branch


coverage (2)
class X {
. . .
void f(bool x) { Two test cases
. . .
if (x) { class XTest {
. . . . . . Is reusing x
} void testf() { a good idea?
X x;
. . .
x.f(true);
}
CPPUNIT_ASSERT(…);
}; . . .
All control
x.f(false);
transfers are
CPPUNIT_ASSERT(…);
exercised
}
. . .
10
};
Unit Testing Example: Branch
coverage (3)
class X {
. . .
void f(int x) {
. . .
do {
x--;
. . . class XTest {
} . . .
while (x>0); void testf() {
. . .
X x;
x.f(0);
}
CPPUNIT_ASSERT(…);
Statement
}; coverage? Yes }
. . .
Branch coverage? No }; 11

Advantages of Unit Testing


z Write test = understand better what has to be done
z Silly bugs appear immediately = less time spent on
debugging
z Provide a working specification of your functional
code
z Speed-up the development – write test once, use
test to find errors many times
z Gain confidence in your code
z Repeatable and deterministic
z Regression tests – incorrect changes discovered
immediately; unautomated refactoring enabled
Encouraged by eXtreme Programming community
12
Old-fashioned Low-level
Testing
z Stepping through a debugger
z drawbacks:
1. not automatic
2. time-consuming

z Littering code with stream output calls


z drawbacks:
1. makes code ugly
(ok, nowadays you could use aspects to avoid it;-))

2. generates to much information


13

What is xUnit?
z An automated unit test framework
z Provides the Driver for unit(s)
z Provides automatic test runs
z Provides automatic result checks
z Available for multiple languages:
z JUnit (from Kent Beck (XP) and Erich Gamma(Gang of Four))
z cppUnit
z httpUnit
z NUnit
z …
14
The Goals of JUnit
z To write a framework within which we have some
glimmer of hope that developers will actually write
tests.
z The framework has to use familiar tools, so there is little
new to learn.
z The second goal of testing is creating tests that
retain their value over time.
z Someone other than the original author has to be able to
execute the tests and interpret the results.
z Creating a setup or fixture is expensive
z A framework has to enable reusing fixtures to run different
tests.
15

The Design of JUnit


z Patterns Generate Architectures
z Used to present the design of JUnit.
z The idea is to explain the design of a system by starting
with nothing and applying patterns, one after another, until
you have the architecture of the system.
z Presentation flow
(1) Present the architectural problem to be solved
(2) Summarize the pattern that solves it
(3) Show how the pattern was applied to JUnit.
z The details can refer to JUnit A Cook's Tour
http://junit.sourceforge.net/doc/cookstour/cookstour.htm

16
The Patterns Used in JUnit

17

How to Use JUnit (1)


z Write a test case (Fixture)
z Create your own test case as a subclass of JUnit TestCase
z Add an instance variable for each known object in the fixture
z Override the setUp() method to initialize object(s) under test
z Override the tearDown() method to release object(s) under test
z Run the test
z Define a public test???() method for exercising the object(s)
under test
z Verify the result
z Assert expected result of the test case using assertEquals(),
assertTrue, …
z Clean up the fixture
z through the tearDown() method

18
How to Use JUnit (2)
z Suite management
z A test suite is a collection of test cases that are intended to be
used to show that a program under test has some specified set of
behaviors
z Write a static suite() containing all the test???() in the fixture
class
z Define a main() method that runs the TestCase in batch mode
z Error vs. Failures
z Error: unanticipated problem like an
ArrayIndexOutOfBoundsException
z Failure: is anticipated and can be checked with assertions

19

JUnit FAQ: Best Practices


z Test-first programming
z Tests should be written before the code.
z Test-first programming is practiced by only writing new
code when an automated test is failing.
z When all the tests pass, you know you're done!
z When a bug is reported, first write unit test(s) to expose the
bug(s), then fix them.
z This makes it almost impossible for that particular bug to
resurface later.
z Good tests tell you how to best design the system for its
intended use.
z Test-driven development is a lot more fun than writing tests
after the code seems to be working.
20
JUnit FAQ: Best Practices
z Do I have to write a test for everything?
z No, just test everything that could reasonably break.
z Investments in testing are equal investments in design.
z If defects aren't being reported, and your design responds
well to change, then you're probably testing enough.
z If you're spending a lot of time fixing defects and your
design is difficult to grow, you should write more tests.
z If something is difficult to test, it's usually an opportunity for
a design improvement.

21

JUnit FAQ: Best Practices


z How simple is “too simple to break”?
z If it can't break on its own, it's too simple to break.
z Example
z getX() method cannot break unless the compiler
is also broken. Therefore, don't test getX().
z setX() method is also too simple to break.
However, if it does any parameter validation, you
likely need to test it.

22
JUnit FAQ: Best Practices
z How often should I run my tests?
z Run all your unit tests as often as possible
z Ideally every time the code is changed.
z Make sure all your unit tests always run at 100%.
z Frequent testing gives you confidence that your changes
didn't break anything.
z For larger systems, you may just run specific test suites
that are relevant to the code you're working on.
z Run all the tests of the a system at least once per day (or
night).

23

JUnit FAQ: Best Practices


z What do I do when a defect is reported?
z Write a failing test that exposes the defect
z When the test passes, you know the defect is fixed!

z This is a learning opportunity


z Perhaps the defect could have been prevented by being more
aggressive about testing everything that could reasonably break.
z Why not just use print?
z It requires that output be scanned manually every time the
program is run to ensure that the code is doing what's expected.
z Tests should retain its value over time.
z Why not just use a debugger?
z The same as that of using print.
24
JUnit FAQ: Best Practices
z Testing Idioms
z Code a little, test a little, code a little, test a little...
z Begin by writing tests for the areas of code that you're most
worried about breaking.
z Write tests that have the highest possible return on your
testing investment.
z When you need to add new functionality to the system,
write the tests first.
z If you find yourself debugging using System.out.println(),
write a test case instead.
z The next time someone asks you for help debugging, help
them write a test.
z Don't deliver software that doesn't pass all of its tests.
25

CppUnit Cookbook (1)


z Simple Test Case (Ordinarily, you will not use such simple test)
z Subclass the TestCase class.
z Override the method runTest().
z When you want to check a value, call CPPUNIT_ASSERT(bool)
and pass in an expression that is true if the test succeeds

class ComplexNumberTest : public CppUnit::TestCase {


public:
ComplexNumberTest( std::string name ) :
CppUnit::TestCase( name ) {}

void runTest() {
CPPUNIT_ASSERT( Complex (10, 1) == Complex (10, 1) );
CPPUNIT_ASSERT( !(Complex (1, 1) == Complex (2, 2)) );
}
};
26
CppUnit Cookbook (2)
z Fixture - a known set of objects served as a base for a set of test cases
z To add new tests
z Add member variables for each part of the fixture
z Override setUp() to initialize the variables
z Override tearDown() to release any resources allocated in setUp()

class ComplexNumberTest : public CppUnit::TestFixture {


private:
Complex *m_10_1, *m_1_1, *m_11_2;
protected:
void setUp()
{
m_10_1 = new Complex( 10, 1 );
m_1_1 = new Complex( 1, 1 );
m_11_2 = new Complex( 11, 2 );
}
void tearDown()
{
delete m_10_1;
delete m_1_1;
delete m_11_2;
} 27
};

CppUnit Cookbook (3)


z How do you write and invoke individual tests using a fixture?
z Write the test case as a method in the fixture class
z Create a TestCaller which runs that particular method
class ComplexNumberTest : public CppUnit::TestFixture {
private:
Complex *m_10_1, *m_1_1, *m_11_2;
protected:
void setUp() {...}
void tearDown() {...}
void testEquality()
{
CPPUNIT_ASSERT( *m_10_1 == *m_10_1 );
CPPUNIT_ASSERT( !(*m_10_1 == *m_11_2) );
}
void testAddition()
{
CPPUNIT_ASSERT( *m_10_1 + *m_1_1 == *m_11_2 );
}
};
CppUnit::TestCaller<ComplexNumberTest> test("testEquality",
&ComplexNumberTest::testEquality );
CppUnit::TestResult result;
test.run( &result ); 28
CppUnit Cookbook (4)
z Use TestSuite class that runs any number of TestCases together
CppUnit::TestSuite suite;
CppUnit::TestResult result;
suite.addTest( new CppUnit::TestCaller<ComplexNumberTest>(
"testEquality",
&ComplexNumberTest::testEquality ) );
suite.addTest( new CppUnit::TestCaller<ComplexNumberTest>(
"testAddition",
&ComplexNumberTest::testAddition ) );
suite.run( &result );

z TestSuite can contain any object implementing the Test interface


CppUnit::TestSuite suite;
CppUnit::TestResult result;
suite.addTest( ComplexNumberTest::suite() );
suite.addTest( SurrealNumberTest::suite() );
suite.run( &result );
29

CppUnit Cookbook (5)


z Run TestSuite using TestRunner
z Using Helper Macros
z Defined in cppunit/extensions/HelperMacros.h which must be
integrated for initiating and finishing a test suite
(CPPUNIT_TEST_SUITE and CPPUNIT_TEST_SUITE_END)
z Rewrite the fixture to include the HelperMacros.h
#include <cppunit/extensions/HelperMacros.h>
class ComplexNumberTest : public CppUnit::TestFixture
{...}

z TestSuite can be created with the HelperMacros


CPPUNIT_TEST_SUITE( ComplexNumberTest );
CPPUNIT_TEST( testEquality );
CPPUNIT_TEST( testAddition );
CPPUNIT_TEST_SUITE_END();
30
CppUnit Cookbook (6)
z Run TestSuite using TestRunner
#include <cppunit/ui/text/TestRunner.h>
#include "ComplexNumberTest.h"
int main( int argc, char **argv)
{
CppUnit::TextUi::TestRunner runner;
CPPUNIT_TEST_SUITE( ComplexNumberTest );
CPPUNIT_TEST( testEquality ); ...
CPPUNIT_TEST_SUITE_END();
bool wasSuccessful = runner.run();
return wasSuccessful ? 0 : 1;
}

z If all the tests pass, you'll get an informative message.


z If any fail, you'll get the following information:
z The name of the test case that failed
z The name of the source file that contains the test
z The line number where the failure occurred
z All of the text inside the call to CPPUNIT_ASSERT() which detected the 31
failure

CppUnit Cookbook (7)


z The classes of the CppUnit-framework are placed in a
namespace which is defined by the macro CPPUNIT_NS
z CppUnit macros
z CPPUNIT_ASSERT
z Check whether the passed expression returns the value True
z CPPUNIT_ASSERT_EQUAL
z Check whether the first parameter is like the second one
z CPPUNIT_ASSERT_THROW
z Check whether the passed expression throws an exception of the
passed type
z CppUnit Examples
z See LinkedListTest

32
Unit testing and
Test-driven Development (TDD)
z How to write JUnit/CPPUnit tests? Do it TDD!
z Workflow:
1. Think about functionality to be implemented &
scenarios in which the unit to be tested will play a role.
2. Create a stub of the unit you want to implement.
3. Write a test for each scenario and/or use of the unit.
4. Make the unit fail the tests (nothing is implemented
yet!).
5. Develop the unit until it passes every test.
z Encountered new scenario/use?
z make a test before implementing it!

33

Test-driven Development (TDD)


z Test-driven development (TDD) is an evolutionary
approach to development which combines
z Test-first development
z Write a test before you write just enough production code to
fulfill that test
z Refactoring
z The goal of TDD
z Think through your design before your write your functional
code
z To write clean code that works
z TDD is primarily a design technique with a side effect of
ensuring that your source code is thoroughly unit tested
34
References
z W.-K Chen, “Object-Oriented Programming -
Software testing”
z JUnit A Cook's Tour.
http://junit.sourceforge.net/doc/cookstour/cookstour.
htm
z CppUnit Cookbook.
http://cppunit.sourceforge.net/doc/1.8.0/cppunit_coo
kbook.html
z Krzysztof Pietroszek, “Unit testing with JUnit and
CPPUnit”.
http://swen.uwaterloo.ca/~se2/project/project-
junit.pdf

35

You might also like