Выбрать главу

// A first pass at Date.h

#ifndef DATE_H

#define DATE_H

#include <string>

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 <iostream>

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 nPass and 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 Date class 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 Date class:.

//: C02:SimpleDateTest2.cpp

//{L} Date

#include <iostream>

#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 Date class is available in the files Date.h and Date.cpp in the appendix and on the MindView website.[21][ ]

The TestSuite Framework

Some automated C++ unit test tools are available on the World Wide Web for download, such as CppUnit.[22] 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 TestSuite that contains two key classes: Test and Suite.

The Test class 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 Date class using the framework, you can inherit from Test as 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);

вернуться

21

Our Date class is also “internationalized”, in that it supports wide character sets. This is introduced at the end of the next chapter.