· Dates to be compared need to be able to span an arbitrary number of centuries (for example, 1600–2200).
Your class can store three integers representing the year, month, and day. (Just be sure the year is at least 16 bits in size to satisfy the last bulleted item.) The interface for your Dateclass might look like this: .
// A first pass at Date.h
#ifndef DATE_H
#define DATE_H
#include
class Date {
public:
// A struct to hold elapsed time:
struct Duration {
int years;
int months;
int days;
Duration(int y, int m, int d)
: years(y), months(m), days(d) {}
};
Date();
Date(int year, int month, int day);
Date(const std::string&);
int getYear() const;
int getMonth() const;
int getDay() const;
std::string toString() const;
friend bool operator<(const Date&, const Date&);
friend bool operator>(const Date&, const Date&);
friend bool operator<=(const Date&, const Date&);
friend bool operator>=(const Date&, const Date&);
friend bool operator==(const Date&, const Date&);
friend bool operator!=(const Date&, const Date&);
friend Duration duration(const Date&, const Date&);
};
#endif
Before you even think about implementation, you can solidify your grasp of the requirements for this class by writing the beginnings of a test program. You might come up with something like the following:
//: C02:SimpleDateTest.cpp
//{L} Date
// You’ll need the full Date.h from the Appendix:
#include "Date.h"
#include
using namespace std;
// Test machinery
int nPass = 0, nFail = 0;
void test(bool t) {
if(t) nPass++; else nFail++;
}
int main() {
Date mybday(1951, 10, 1);
test(mybday.getYear() == 1951);
test(mybday.getMonth() == 10);
test(mybday.getDay() == 1);
cout << "Passed: " << nPass << ", Failed: "
<< nFail << endl;
}
/* Expected output:
Passed: 3, Failed: 0
*/ ///:~
In this trivial case, the function test( )maintains the global variables nPassand nFail. The only visual inspection you do is to read the final score. If a test failed, a more sophisticated test( )displays an appropriate message. The framework described later in this chapter has such a test function, among other things .
You can now implement enough of the Dateclass to get these tests to pass, and then you can proceed iteratively in like fashion until all the requirements are met. By writing tests first, you are more likely to think of corner cases that might break your upcoming implementation, and you’re more likely to write the code correctly the first time. Such an exercise might produce the following "final" version of a test for the Dateclass: .
//: C02:SimpleDateTest2.cpp
//{L} Date
#include
#include "Date.h"
using namespace std;
// Test machinery
int nPass = 0, nFail = 0;
void test(bool t) {
if(t) nPass++; else nFail++;
}
int main() {
Date mybday(1951, 10, 1);
Date today;
Date myevebday("19510930");
// Test the operators
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);
// Test the functions
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");
// Test duration
Date d2(2003, 7, 4);
Date::Duration dur = duration(mybday, d2);
test(dur.years == 51);
test(dur.months == 9);
test(dur.days == 3);
// Report results:
cout << "Passed: " << nPass << ", Failed: "
<< nFail << endl;
} ///:~
The word "final" above was quoted because this test can of course be more fully developed. For example we haven’t tested that long durations are handled correctly. To save space on the printed page we’ll stop here, but you get the idea. The full implementation for the Dateclass is available in the files Date.hand Date.cppin the appendix and on the MindView website. [21] Our Date class is also “internationalized”, in that it supports wide character sets. This is introduced at the end of the next chapter.
[ ]
Some automated C++ unit test tools are available on the World Wide Web for download, such as CppUnit. [22] See http://sourceforge.net/projects/cppunit for more information.
These are well designed and implemented, but our purpose here is not only to present a test mechanism that is easy to use, but also easy to understand internally and even tweak if necessary. So, in the spirit of "TheSimplestThingThatCouldPossiblyWork," we have developed the TestSuite Framework , a namespace named TestSuitethat contains two key classes: Testand Suite .
The Testclass is an abstract class you derive from to define a test object. It keeps track of the number of passes and failures for you and displays the text of any test condition that fails. Your main task in defining a test is 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 Dateclass using the framework, you can inherit from Testas shown in the following program:
//: C02:DateTest.h
#ifndef DATE_TEST_H
#define DATE_TEST_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 ///:~
Running the test is a simple matter of instantiating a DateTestobject and calling its run( )member function .
//: C02:DateTest.cpp
// Automated Testing (with a Framework)
//{L} Date ../TestSuite/Test
#include
#include "DateTest.h"
using namespace std;
Читать дальше