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

The test framework code

The test framework code library is in a subdirectory called TestSuite in the code distribution available on the MindView website. To use it, include the search path for the TestSuite subdirectory in your header, link the object files, and include the TestSuite subdirectory in the library search path. Here is the header for Test.h:

//: TestSuite:Test.h

#ifndef TEST_H

#define TEST_H

#include <string>

#include <iostream>

#include <cassert>

using std::string;

using std::ostream;

using std::cout;

// The following have underscores because

// they are macros. For consistency,

// succeed_() also has an underscore.

#define test_(cond) \

  do_test(cond, #cond, __FILE__, __LINE__)

#define fail_(str) \

  do_fail(str, __FILE__, __LINE__)

namespace TestSuite {

class Test {

public:

  Test(ostream* osptr = &cout);

  virtual ~Test(){}

  virtual void run() = 0;

  long getNumPassed() const;

  long getNumFailed() const;

  const ostream* getStream() const;

  void setStream(ostream* osptr);

  void succeed_();

  long report() const;

  virtual void reset();

protected:

  void do_test(bool cond, const string& lbl,

    const char* fname, long lineno);

  void do_fail(const string& lbl,

    const char* fname, long lineno);

private:

  ostream* osptr;

  long nPass;

  long nFail;

  // Disallowed:

  Test(const Test&);

  Test& operator=(const Test&);

};

inline Test::Test(ostream* osptr) {

  this->osptr = osptr;

  nPass = nFail = 0;

}

inline long Test::getNumPassed() const {

  return nPass;

}

inline long Test::getNumFailed() const {

  return nFail;

}

inline const ostream* Test::getStream() const {

  return osptr;

}

inline void Test::setStream(ostream* osptr) {

  this->osptr = osptr;

}

inline void Test::succeed_() {

  ++nPass;

}

inline void Test::reset() {

  nPass = nFail = 0;

}

} // namespace TestSuite

#endif // TEST_H ///:~

There are three virtual functions in the Test class:

·         A virtual destructor

·         The function reset( )

·         The pure virtual function run( )

As explained in Volume 1, it is an error to delete a derived heap object through a base pointer unless the base class has a virtual destructor. Any class intended to be a base class (usually evidenced by the presence of at least one other virtual function) should have a virtual destructor. The default implementation of the Test::reset( ) resets the success and failure counters to zero. You might want to override this function to reset the state of the data in your derived test object; just be sure to call Test::reset( ) explicitly in your override so that the counters are reset. The Test::run( ) member function is pure virtual, of course, since you are required to override it in your derived class.

The test_( ) and fail_( ) macros can include file name and line number information available from the preprocessor. We originally omitted the trailing underscores in the names, but the original fail( ) macro collided with ios::fail( ), causing all kinds of compiler errors.

Here is the implementation of Test:

//: TestSuite:Test.cpp {O}

#include "Test.h"

#include <iostream>

#include <typeinfo>  // Note: Visual C++ requires /GR

using namespace std;

using namespace TestSuite;

void Test::do_test(bool cond,

  const std::string& lbl, const char* fname,

  long lineno) {

  if (!cond)

    do_fail(lbl, fname, lineno);

  else

    succeed_();

}

void Test::do_fail(const std::string& lbl,

  const char* fname, long lineno) {

  ++nFail;

  if (osptr) {

    *osptr << typeid(*this).name()

           << "failure: (" << lbl << ") , "

           << fname

           << " (line " << lineno << ")\n";

  }

}

long Test::report() const {

  if (osptr) {

    *osptr << "Test \"" << typeid(*this).name()

           << "\":\n\tPassed: " << nPass

           << "\tFailed: " << nFail

           << endl;

  }

  return nFail;

} ///:~

No rocket science here. The Test class just keeps track of the number of successes and failures as well as the stream where you want Test::report( ) to display the results. The test_( ) and fail_( ) macros extract the current file name and line number information from the preprocessor and pass the file name to do_test( ) and the line number to do_fail( ), which do the actual work of displaying a message and updating the appropriate counter. We can’t think of a good reason to allow copy and assignment of test objects, so we have disallowed these operations by making their prototypes private and omitting their respective function bodies.

Here is the header file for Suite:.

//: TestSuite:Suite.h

#ifndef SUITE_H

#define SUITE_H

#include "../TestSuite/Test.h"

#include <vector>

#include <stdexcept>

using std::vector;

using std::logic_error;

namespace TestSuite {

class TestSuiteError : public logic_error {

public:

  TestSuiteError(const string& s = "")

    : logic_error(s) {}

};

class Suite {

public:

  Suite(const string& name, ostream* osptr = &cout);

  string getName() const;

  long getNumPassed() const;

  long getNumFailed() const;

  const ostream* getStream() const;

  void setStream(ostream* osptr);

  void addTest(Test* t) throw (TestSuiteError);

  void addSuite(const Suite&);

  void run();  // Calls Test::run() repeatedly

  long report() const;

  void free();  // Deletes tests