A.1 Example 1: Create a Book
The first example creates the class
Book
. En route, the unit test framework is built and the first unit test written.
A.1.1 Step 0: Set Up the Unit Test Framework
The framework initially is built on a single class,
UnitTest
, shown in Figure A-1. The source code for
UnitTest
includes a header file,
UnitTest.h
, and an implementation file,
UnitTest.cpp
, as shown in Example A-1.
Figure A-1. The class UnitTest
Example A-1. The class UnitTest
UnitTest.h
#define UT_ASSERT( condition ) \
assertTrue(condition,__FILE__,__LINE_ _,#condition)
class
UnitTest
{
public:
virtual ~UnitTest( ) {}
virtual void
runTest
( ) = 0;
protected:
void
assertTrue
(bool condition, const char *file,
int line, const char *msg);
static int
num_test_success
;
};
UnitTest.cpp
#include <stdio.h>
#include <stdlib.h>
#include "UnitTest.h"
int UnitTest::
num_test_success
= 0;
void UnitTest::
assertTrue
(bool condition,
const char *file, int line,
const char *msg) {
if (!(condition)) {
printf("FAILURE!\n");
printf("%s:%d:%s\n", file, line, msg);
exit(1);
}
++num_test_success;
}
UnitTest
is an abstract class because it contains the pure virtual function
runTest( )
. Actual unit tests will be inherited from
UnitTest
and must override the
runTest( )
method. The function
assertTrue( )
should be used to test Boolean conditions in
runTest( )
. If the condition is
TRUE
, the counter
num_test_success
is incremented. If it is
FALSE
, the function
test_failure()
is called. This function
reports
the file location of the failure and exits. The macro
UT_ASSERT()
is used in place of direct calls to
assertTrue( )
. It uses the preprocessor directives
__FILE_ _
and
__LINE_ _
to fill in the location of the call at compilation, and the
#condition
argument allows the conditional expression to be printed in the failure report.
Compile
UnitTest
with the command
g++
-c UnitTest.cpp
(or your compiler's equivalent command).
A.1.2 Step 1: Create a Unit Test
Start by creating the unit test class
BookTest
, as shown in Example A-2.
Example A-2. The definition of the class BookTest
BookTest.h
#include "UnitTest.h"
#include "Book.h"
class
BookTest
: public
UnitTest
{
public:
void
runTest
( ) {
Book book("Cosmos");
UT_ASSERT(!strcmp(book.title, "Cosmos"));
}
};
The unit test
BookTest
constructs an instance of the class
Book
, passing the title as an argument, and then tests the value of the book's
title
attribute. Since the entire implementation of
BookTest
is present in
BookTest.h
, no
.cpp
file is necessary.
BookTest
is
instantiated
and run by a class called
TestRunner
, which also contains the
main( )
method for the test framework. Example A-3 shows the implementation of
TestRunner
.
Example A-3. The class TestRunner
TestRunner.cpp
#include "stdio.h"
#include "BookTest.h"
int
main
( ) {
BookTest test;
test.runTest( );
printf("SUCCESS!\n");
return 0;
}
Compile
TestRunner
. The compiler will report that it cannot find the file
Book.h
, and that the class
Book
is undeclared. No surprise there! So, the
next
step is to create the most basic implementation of the class
Book
that will allow the code to compile, as shown in Example A-4.
Example A-4. Initial version of the class Book
Book.h
#include "string.h"
class
Book
{
public:
Book(const char* title) {}
char title[255];
};
The class
TestRunner
should compile now. The test framework and the unit test are in place. To run the test, link the objects together into an executable:
g++ -o TestRunner TestRunner.o UnitTest.o
Execute
TestRunner
. The following result is
reported
:
FAILURE!
BookTest.h:9:!strcmp(book.title, "Cosmos")
This failure
demonstrates
that the unit test framework is working. Note how the
UT_ASSERT()
macro captures the file's
name
and location, as well as the code contents of the test assertion.
A.1.3 Step 2: Create a Book
BookTest
fails because
Book
does not yet contain the functionality being
tested
. In this step, the minimum necessary code to achieve unit test success is added.
The constructor for
Book
is changed to set the title attribute, as shown in Example A-5.
Example A-5. The class Book with title attribute set by the constructor
Book.h
#include "string.h"
class Book {
public:
Book(const char* title) {
strcpy(this->title, title);
}
char title[255];
};
A.1.4 Step 3: Test Again
The final step is to rebuild the code, re-run the unit test, and see whether the changes produce the desired results.
Rebuild and execute
TestRunner
. The results of
BookTest
are reported:
SUCCESS!
An instance of
Book
can now be created and given a title.
|