Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
Some automated C++ unit test tools are available on the
World Wide Web for download, such as CppUnit. Our
purpose here is not only to present a test mechanism that is easy to use, but
also easy to understand internally and even modify if necessary. So, in the
spirit of Do The Simplest Thing That Could Possibly Work, we
have developed the TestSuite Framework, a namespace named TestSuite
that contains two key classes: Test and Suite.
The Test class is an abstract base class from which you
derive a test object. It keeps track of the number of passes and failures and
displays the text of any test condition that fails. You simply to override the run( )
member function, which should in turn call the test_( ) macro for
each Boolean test condition you define.
To define a test for the Date class using the
framework, you can inherit from Test as shown in the following program:
//: C02:DateTest.h
#ifndef DATETEST_H
#define DATETEST_H
#include "Date.h"
#include "../TestSuite/Test.h"
class DateTest : public TestSuite::Test {
Date mybday;
Date today;
Date myevebday;
public:
DateTest(): mybday(1951, 10, 1),
myevebday("19510930") {}
void run() {
testOps();
testFunctions();
testDuration();
}
void testOps() {
test_(mybday < today);
test_(mybday <= today);
test_(mybday != today);
test_(mybday == mybday);
test_(mybday >= mybday);
test_(mybday <= mybday);
test_(myevebday < mybday);
test_(mybday > myevebday);
test_(mybday >= myevebday);
test_(mybday != myevebday);
}
void testFunctions() {
test_(mybday.getYear() == 1951);
test_(mybday.getMonth() == 10);
test_(mybday.getDay() == 1);
test_(myevebday.getYear() == 1951);
test_(myevebday.getMonth() == 9);
test_(myevebday.getDay() == 30);
test_(mybday.toString() == "19511001");
test_(myevebday.toString() ==
"19510930");
}
void testDuration() {
Date d2(2003, 7, 4);
Date::Duration dur = duration(mybday, d2);
test_(dur.years == 51);
test_(dur.months == 9);
test_(dur.days == 3);
}
};
#endif // DATETEST_H ///:~
Running the test is a simple matter of instantiating a DateTest
object and calling its run( ) member function:
//: C02:DateTest.cpp
// Automated testing (with a framework).
//{L} Date ../TestSuite/Test
#include <iostream>
#include "DateTest.h"
using namespace std;
int main() {
DateTest test;
test.run();
return test.report();
}
/* Output:
Test "DateTest":
Passed: 21, Failed: 0
*/ ///:~
The Test::report( ) function displays the
previous output and returns the number of failures, so it is suitable to use as
a return value from main( ).
The Test class uses RTTI to
get the name of your class (for example, DateTest) for the report. There
is also a setStream( ) member function if you want the test results
sent to a file instead of to the standard output (the default). You ll see the Test
class implementation later in this chapter.
The test_( ) macro can extract the text of the
Boolean condition that fails, along with its file name and line number. To see what
happens when a failure occurs, you can introduce an intentional error in the
code, for example by reversing the condition in the first call to test_( )
in DateTest::testOps( ) in the previous example code. The output
indicates exactly what test was in error and where it happened:
DateTest failure: (mybday > today) , DateTest.h
(line 31)
Test "DateTest":
Passed: 20 Failed: 1
In addition to test_( ), the framework includes
the functions succeed_( ) and fail_( ), for cases where
a Boolean test won t do. These functions apply when the class you re testing
might throw exceptions. During testing, create an input set that will cause the
exception to occur. If it doesn t, it s an error and you call fail_( )
explicitly to display a message and update the failure count. If it does throw
the exception as expected, you call succeed_( ) to update the
success count.
To illustrate, suppose we modify the specification of the
two non-default Date constructors to throw a DateError exception
(a type nested inside Date and derived from std::logic_error) if
the input parameters do not represent a valid date:
Date(const string& s) throw(DateError);
Date(int year, int month, int day) throw(DateError);
The DateTest::run( ) member function can now
call the following function to test the exception handling:
void testExceptions() {
try {
Date d(0,0,0); // Invalid
fail_("Invalid date undetected in Date int
ctor");
} catch(Date::DateError&) {
succeed_();
}
try {
Date d(""); // Invalid
fail_("Invalid date undetected in Date
string ctor");
} catch(Date::DateError&) {
succeed_();
}
}
In both cases, if an exception is not thrown, it is an
error. Notice that you must manually pass a message to fail_( ),
since no Boolean expression is being evaluated.
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |