目前网上搜索到的讲CppUnit源码解读基本来源于晨光CppUnit源码解读,是很好的源码阅读笔记。但是针对的是
CppUnit1.8.0版,现在网上下载到的一般都比这个新了,我下载的是1.12.1版,代码有一些变动,就结合晨光的
笔记仔细看了一些源码。
首先看一下我们通常写的简单的测试类:
class MyTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE( MyTest );
CPPUNIT_TEST( testEqual );
CPPUNIT_TEST_SUITE_END();
public:
void testEqual();
};
这个测试类里面只包括一个我们想测试的testEqual()方法。里面有三个主要的宏,先介绍基类TestFixture,
再逐一介绍宏。三个宏定义摘自HelpMacros.h。
class CPPUNIT_API TestFixture
{
public:
virtual ~TestFixture() {};
//! \brief Set up context before running a test.
virtual void setUp() {};
//! Clean up after the test run.
virtual void tearDown() {};
};
没啥东西,setUp和tearDown其他资料都已经讲过了,无外乎提供一个测试环境的创建工作和测试后的打扫战场。
/*! \brief Begin test suite
*
* This macro starts the declaration of a new test suite.
* Use CPPUNIT_TEST_SUB_SUITE() instead, if you wish to include the
* test suite of the parent class.
*
* \param ATestFixtureType Type of the test case class. This type \b MUST
* be derived from TestFixture.
* \see CPPUNIT_TEST_SUB_SUITE, CPPUNIT_TEST, CPPUNIT_TEST_SUITE_END,
* \see CPPUNIT_TEST_SUITE_REGISTRATION, CPPUNIT_TEST_EXCEPTION, CPPUNIT_TEST_FAIL.
*/
#define CPPUNIT_TEST_SUITE( ATestFixtureType ) \
public: \
typedef ATestFixtureType TestFixtureType; \
\
private: \
static const CPPUNIT_NS::TestNamer &getTestNamer__() \
{ \
static CPPUNIT_TESTNAMER_DECL( testNamer, ATestFixtureType ); \
return testNamer; \
} \
\
public: \
typedef CPPUNIT_NS::TestSuiteBuilderContext<TestFixtureType> \
TestSuiteBuilderContextType; \
\
static void \
addTestsToSuite( CPPUNIT_NS::TestSuiteBuilderContextBase &baseContext ) \
{ \
TestSuiteBuilderContextType context( baseContext )
上面宏定义的函数没有完,余下的要看CPPUNIT_TEST和CPPUNIT_TEST_SUITE_END。我们先看上面的宏内容。
一个typedef是给ATestFixtureType起个更好看的名字;然后定义一个static的getTestNamer__函数,函数里面用了宏CPPUNIT_TESTNAMER_DECL,这个宏定义在TestNamer.h里面。
/*! \def CPPUNIT_TESTNAMER_DECL( variableName, FixtureType )
* \brief Declares a TestNamer.
*
* Declares a TestNamer for the specified type, using RTTI if enabled, otherwise
* using macro string expansion.
*
* RTTI is used if CPPUNIT_USE_TYPEINFO_NAME is defined and not null.
*
* \code
* void someMethod()
* {
* CPPUNIT_TESTNAMER_DECL( namer, AFixtureType );
* std::string fixtureName = namer.getFixtureName();
* ...
* \endcode
*
* \relates TestNamer
* \see TestNamer
*/
#if CPPUNIT_USE_TYPEINFO_NAME
# define CPPUNIT_TESTNAMER_DECL( variableName, FixtureType ) \
CPPUNIT_NS::TestNamer variableName( typeid(FixtureType) )
#else
# define CPPUNIT_TESTNAMER_DECL( variableName, FixtureType ) \
CPPUNIT_NS::TestNamer variableName( std::string(#FixtureType) )
#endif
结合TestNamer.h里面TestNamer类的定义:
/*! \brief Names a test or a fixture suite.
*
* TestNamer is usually instantiated using CPPUNIT_TESTNAMER_DECL.
*
*/
class CPPUNIT_API TestNamer
{
public:
#if CPPUNIT_HAVE_RTTI
/*! \brief Constructs a namer using the fixture's type-info.
* \param typeInfo Type-info of the fixture type. Use to name the fixture suite.
*/
TestNamer( const std::type_info &typeInfo );
#endif
/*! \brief Constructs a namer using the specified fixture name.
* \param fixtureName Name of the fixture suite. Usually extracted using a macro.
*/
TestNamer( const std::string &fixtureName );
virtual ~TestNamer();
/*! \brief Returns the name of the fixture.
* \return Name of the fixture.
*/
virtual std::string getFixtureName() const;
/*! \brief Returns the name of the test for the specified method.
* \param testMethodName Name of the method that implements a test.
* \return A string that is the concatenation of the test fixture name
* (returned by getFixtureName()) and\a testMethodName,
* separated using '::'. This provides a fairly unique name for a given
* test.
*/
virtual std::string getTestNameFor( const std::string &testMethodName ) const;
protected:
std::string m_fixtureName;
};
我们很容易知道getTestNamer__函数就是声明了一个static的TestNamer的实例叫testNamer,该实例内部的m_fixtureName就是测试类的名字,也就是说static的testNamer变量是用来保存测试类名字的。
下面的一个typedef用到了TestSuiteBuilderContext,定义在TestSuiteBuilderContext.h里面,我们看其定义:
/*! \brief Type-sage context used when creating test suite in HelperMacros.
*
* \sa TestSuiteBuilderContextBase.
*/
template<class Fixture>
class TestSuiteBuilderContext : public TestSuiteBuilderContextBase
{
public:
typedef Fixture FixtureType;
TestSuiteBuilderContext( TestSuiteBuilderContextBase &contextBase )
: TestSuiteBuilderContextBase( contextBase )
{
}
/*! \brief Returns a new TestFixture instance.
* \return A new fixture instance. The fixture instance is returned by
* the TestFixtureFactory passed on construction. The actual type
* is that of the fixture on which the static method suite()
* was called.
*/
FixtureType *makeFixture() const
{
return CPPUNIT_STATIC_CAST( FixtureType *,
TestSuiteBuilderContextBase::makeTestFixture() );
}
};
该类继承自TestSuiteBuilderContextBase,其中有个makeFixture函数用到了宏CPPUNIT_STATIC_CAST,先不管它,咱么继续寻根究底基类TestSuiteBuilderContextBase:
/*! \brief Context used when creating test suite in HelperMacros.
*
* Base class for all context used when creating test suite. The
* actual context type during test suite creation is TestSuiteBuilderContext.
*
* \sa CPPUNIT_TEST_SUITE, CPPUNIT_TEST_SUITE_ADD_TEST,
* CPPUNIT_TEST_SUITE_ADD_CUSTOM_TESTS.
*/
class CPPUNIT_API TestSuiteBuilderContextBase
{
public:
/*! \brief Constructs a new context.
*
* You should not use this. The context is created in
* CPPUNIT_TEST_SUITE().
*/
TestSuiteBuilderContextBase( TestSuite &suite,
const TestNamer &namer,
TestFixtureFactory &factory );
virtual ~TestSuiteBuilderContextBase();
/*! \brief Adds a test to the fixture suite.
*
* \param test Test to add to the fixture suite. Must not be \c NULL.
*/
void addTest( Test *test );
/*! \brief Returns the fixture name.
* \return Fixture name. It is the name used to name the fixture
* suite.
*/
std::string getFixtureName() const;
/*! \brief Returns the name of the test for the specified method.
*
* \param testMethodName Name of the method that implements a test.
* \return A string that is the concatenation of the test fixture name
* (returned by getFixtureName()) and\a testMethodName,
* separated using '::'. This provides a fairly unique name for a given
* test.
*/
std::string getTestNameFor( const std::string &testMethodName ) const;
/*! \brief Adds property pair.
* \param key PropertyKey string to add.
* \param value PropertyValue string to add.
*/
void addProperty( const std::string &key,
const std::string &value );
/*! \brief Returns property value assigned to param key.
* \param key PropertyKey string.
*/
const std::string getStringProperty( const std::string &key ) const;
protected:
TestFixture *makeTestFixture() const;
// Notes: we use a vector here instead of a map to work-around the
// shared std::map in dll bug in VC6.
// See http://www.dinkumware.com/vc_fixes.html for detail.
typedef std::pair<std::string,std::string> Property;
typedef CppUnitVector<Property> Properties;
TestSuite &m_suite;
const TestNamer &m_namer;
TestFixtureFactory &m_factory;
private:
Properties m_properties;
};
看出一个context其实是包含了一个TestSuite、一个TestFixtureFactory和一个TestNamer并且定义了一组操作,可以向suite里面添加test、得到测试的名字、生成测试用的TestFixture以及其他的一些属性。可以简单看一下Base类中这几个操作的实现:
void
TestSuiteBuilderContextBase::addTest( Test *test )
{
m_suite.addTest( test );
}
std::string
TestSuiteBuilderContextBase::getFixtureName() const
{
return m_namer.getFixtureName();
}
std::string
TestSuiteBuilderContextBase::getTestNameFor(
const std::string &testMethodName ) const
{
return m_namer.getTestNameFor( testMethodName );
}
TestFixture *
TestSuiteBuilderContextBase::makeTestFixture() const
{
return m_factory.makeFixture();
}
可以看出具体操作都是交给这三个物件去完成的。回过头看派生类TestSuiteBuilderContext类里面的makeFixture函数的重新实现,里面的CPPUNIT_STATIC_CAST宏定义,在Portability.h里面:
// If CPPUNIT_HAVE_CPP_CAST is defined, then c++ style cast will be used,
// otherwise, C style cast are used.
#if defined( CPPUNIT_HAVE_CPP_CAST )
# define CPPUNIT_CONST_CAST( TargetType, pointer ) \
const_cast<TargetType>( pointer )
# define CPPUNIT_STATIC_CAST( TargetType, pointer ) \
static_cast<TargetType>( pointer )
#else // defined( CPPUNIT_HAVE_CPP_CAST )
# define CPPUNIT_CONST_CAST( TargetType, pointer ) \
((TargetType)( pointer ))
# define CPPUNIT_STATIC_CAST( TargetType, pointer ) \
((TargetType)( pointer ))
#endif // defined( CPPUNIT_HAVE_CPP_CAST )
可以看出这个仅仅是考虑移植需要定义的一个宏,我们可以理解成基类产生的Fixture再给他强制类型转换一下,现在还不太理解为什么。
现在再回溯看CPPUNIT_TEST_SUITE的定义,它又定义了一个addTestToSuite函数,还没定义完整,先放下,看第二个宏CPPUNIT_TEST(在HelpMacros.h里面):
/*! \brief Add a method to the suite.
* \param testMethod Name of the method of the test case to add to the
* suite. The signature of the method must be of
* type: void testMethod();
* \see CPPUNIT_TEST_SUITE.
*/
#define CPPUNIT_TEST( testMethod ) \
CPPUNIT_TEST_SUITE_ADD_TEST( \
( new CPPUNIT_NS::TestCaller<TestFixtureType>( \
context.getTestNameFor( #testMethod), \
&TestFixtureType::testMethod, \
context.makeFixture() ) ) )
注释已经很清楚了这个宏的作用,用来向suite添加要测试的函数,注意这些函数必须是void类型,不带参数(?)。该宏有调了另一个宏CPPUNIT_TEST_SUITE_ADD_TEST,先不管,我们看看新出现的TestCaller模板类是啥玩意(TestCaller.h):
template <class Fixture>
class TestCaller : public TestCase
{
typedef void (Fixture::*TestMethod)();
public:
/*!
* Constructor for TestCaller. This constructor builds a new Fixture
* instance owned by the TestCaller.
* \param name name of this TestCaller
* \param test the method this TestCaller calls in runTest()
*/
TestCaller( std::string name, TestMethod test ) :
TestCase( name ),
m_ownFixture( true ),
m_fixture( new Fixture() ),
m_test( test )
{
}
/*!
* Constructor for TestCaller.
* This constructor does not create a new Fixture instance but accepts
* an existing one as parameter. The TestCaller will not own the
* Fixture object.
* \param name name of this TestCaller
* \param test the method this TestCaller calls in runTest()
* \param fixture the Fixture to invoke the test method on.
*/
TestCaller(std::string name, TestMethod test, Fixture& fixture) :
TestCase( name ),
m_ownFixture( false ),
m_fixture( &fixture ),
m_test( test )
{
}
/*!
* Constructor for TestCaller.
* This constructor does not create a new Fixture instance but accepts
* an existing one as parameter. The TestCaller will own the
* Fixture object and delete it in its destructor.
* \param name name of this TestCaller
* \param test the method this TestCaller calls in runTest()
* \param fixture the Fixture to invoke the test method on.
*/
TestCaller(std::string name, TestMethod test, Fixture* fixture) :
TestCase( name ),
m_ownFixture( true ),
m_fixture( fixture ),
m_test( test )
{
}
~TestCaller()
{
if (m_ownFixture)
delete m_fixture;
}
void runTest()
{
// try {
(m_fixture->*m_test)();
// }
// catch ( ExpectedException & ) {
// return;
// }
// ExpectedExceptionTraits<ExpectedException>::expectedException();
}
void setUp()
{
m_fixture->setUp ();
}
void tearDown()
{
m_fixture->tearDown ();
}
std::string toString() const
{
return "TestCaller " + getName();
}
private:
TestCaller( const TestCaller &other );
TestCaller &operator =( const TestCaller &other );
private:
bool m_ownFixture;
Fixture *m_fixture;
TestMethod m_test;
};
类继承子TestCase,一开始先typedef了一个指向要测试类的成员函数的指针类型;然后可以看到该类的成员变量m_fixture(要测试的Fixture)和m_test(指向该Fixture上要测试的函数的函数指针),也就是说TestCaller绑定了要测试的函数和TestFixture;看到runTest函数我们就可以猜测最终执行的测试是在这里做的(用成员变量调用函数指针指向的成员函数)。我们再看看基类TestCase,穷根溯源:
*! \brief A single test object.
*
* This class is used to implement a simple test case: define a subclass
* that overrides the runTest method.
*
* You don't usually need to use that class, but TestFixture and TestCaller instead.
*
* You are expected to subclass TestCase if you need to write a class similiar
* to TestCaller.
*/
class CPPUNIT_API TestCase : public TestLeaf, public TestFixture
{
public:
TestCase( const std::string &name );
TestCase();
~TestCase();
virtual void run(TestResult *result);
std::string getName() const;
//! FIXME: this should probably be pure virtual.
virtual void runTest();
private:
TestCase( const TestCase &other );
TestCase &operator=( const TestCase &other );
private:
const std::string m_name;
};
我们猜测m_name就是test case的名字了,还有一个重要的run函数我们不知道干嘛的,看你TestCase.cpp里的实现(移除了大段注释掉的代码):
/// Run the test and catch any exceptions that are triggered by it
void
TestCase::run( TestResult *result )
{
result->startTest(this);
if ( result->protect( TestCaseMethodFunctor( this, &TestCase::setUp ),
this,
"setUp() failed" ) )
{
result->protect( TestCaseMethodFunctor( this, &TestCase::runTest ),
this );
}
result->protect( TestCaseMethodFunctor( this, &TestCase::tearDown ),
this,
"tearDown() failed" );
result->endTest( this );
}
好了,到现在我们还有几个类没有看:TestLeaf,TestResult。
看TestLeaf在TestLeaf.h里面的定义:
*! \brief A single test object.
*
* Base class for single test case: a test that doesn't have any children.
*
*/
class CPPUNIT_API TestLeaf: public Test
{
public:
/*! Returns 1 as the default number of test cases invoked by run().
*
* You may override this method when many test cases are invoked (RepeatedTest
* for example).
*
* \return 1.
* \see Test::countTestCases().
*/
int countTestCases() const;
/*! Returns the number of child of this test case: 0.
*
* You should never override this method: a TestLeaf as no children by definition.
*
* \return 0.
*/
int getChildTestCount() const;
/*! Always throws std::out_of_range.
* \see Test::doGetChildTestAt().
*/
Test *doGetChildTestAt( int index ) const;
};
很容易理解,再看它的基类Test:(Test.h)
/*! \brief Base class for all test objects.
* \ingroup BrowsingCollectedTestResult
*
* All test objects should be a subclass of Test. Some test objects,
* TestCase for example, represent one individual test. Other test
* objects, such as TestSuite, are comprised of several tests.
*
* When a Test is run, the result is collected by a TestResult object.
*
* \see TestCase
* \see TestSuite
*/
class CPPUNIT_API Test
{
public:
virtual ~Test() {};
/*! \brief Run the test, collecting results.
*/
virtual void run( TestResult *result ) =0;
/*! \brief Return the number of test cases invoked by run().
*
* The base unit of testing is the class TestCase. This
* method returns the number of TestCase objects invoked by
* the run() method.
*/
virtual int countTestCases () const =0;
/*! \brief Returns the number of direct child of the test.
*/
virtual int getChildTestCount() const =0;
/*! \brief Returns the child test of the specified index.
*
* This method test if the index is valid, then call doGetChildTestAt() if
* the index is valid. Otherwise std::out_of_range exception is thrown.
*
* You should override doGetChildTestAt() method.
*
* \param index Zero based index of the child test to return.
* \return Pointer on the test. Never \c NULL.
* \exception std::out_of_range is \a index is < 0 or >= getChildTestCount().
*/
virtual Test *getChildTestAt( int index ) const;
/*! \brief Returns the test name.
*
* Each test has a name. This name may be used to find the
* test in a suite or registry of tests.
*/
virtual std::string getName () const =0;
/*! \brief Finds the test with the specified name and its parents test.
* \param testName Name of the test to find.
* \param testPath If the test is found, then all the tests traversed to access
* \a test are added to \a testPath, including \c this and \a test.
* \return \c true if a test with the specified name is found, \c false otherwise.
*/
virtual bool findTestPath( const std::string &testName,
TestPath &testPath ) const;
/*! \brief Finds the specified test and its parents test.
* \param test Test to find.
* \param testPath If the test is found, then all the tests traversed to access
* \a test are added to \a testPath, including \c this and \a test.
* \return \c true if the specified test is found, \c false otherwise.
*/
virtual bool findTestPath( const Test *test,
TestPath &testPath ) const;
/*! \brief Finds the test with the specified name in the hierarchy.
* \param testName Name of the test to find.
* \return Pointer on the first test found that is named \a testName. Never \c NULL.
* \exception std::invalid_argument if no test named \a testName is found.
*/
virtual Test *findTest( const std::string &testName ) const;
/*! \brief Resolved the specified test path with this test acting as 'root'.
* \param testPath Test path string to resolve.
* \return Resolved TestPath.
* \exception std::invalid_argument if \a testPath could not be resolved.
* \see TestPath.
*/
virtual TestPath resolveTestPath( const std::string &testPath ) const;
protected:
/*! Throws an exception if the specified index is invalid.
* \param index Zero base index of a child test.
* \exception std::out_of_range is \a index is < 0 or >= getChildTestCount().
*/
virtual void checkIsValidIndex( int index ) const;
/*! \brief Returns the child test of the specified valid index.
* \param index Zero based valid index of the child test to return.
* \return Pointer on the test. Never \c NULL.
*/
virtual Test *doGetChildTestAt( int index ) const =0;
};
好了,从类前面的注释我们知道一个TestCase仅仅包含一个测试,TestSuite则可以包含许多,看其定义:
/*! \brief A Composite of Tests.
* \ingroup CreatingTestSuite
*
* It runs a collection of test cases. Here is an example.
* \code
* CppUnit::TestSuite *suite= new CppUnit::TestSuite();
* suite->addTest(new CppUnit::TestCaller<MathTest> (
* "testAdd", testAdd));
* suite->addTest(new CppUnit::TestCaller<MathTest> (
* "testDivideByZero", testDivideByZero));
* \endcode
* Note that \link TestSuite TestSuites \endlink assume lifetime
* control for any tests added to them.
*
* TestSuites do not register themselves in the TestRegistry.
* \see Test
* \see TestCaller
*/
class CPPUNIT_API TestSuite : public TestComposite
{
public:
/*! Constructs a test suite with the specified name.
*/
TestSuite( std::string name = "" );
~TestSuite();
/*! Adds the specified test to the suite.
* \param test Test to add. Must not be \c NULL.
*/
void addTest( Test *test );
/*! Returns the list of the tests (DEPRECATED).
* \deprecated Use getChildTestCount() & getChildTestAt() of the
* TestComposite interface instead.
* \return Reference on a vector that contains the tests of the suite.
*/
const CppUnitVector<Test *> &getTests() const;
/*! Destroys all the tests of the suite.
*/
virtual void deleteContents();
int getChildTestCount() const;
Test *doGetChildTestAt( int index ) const;
private:
CppUnitVector<Test *> m_tests;
};
从这个代码我们能看出来用了Composition设计模式……接着看TestComposite类吧:
/*! \brief A Composite of Tests.
*
* Base class for all test composites. Subclass this class if you need to implement
* a custom TestSuite.
*
* \see Test, TestSuite.
*/
class CPPUNIT_API TestComposite : public Test
{
public:
TestComposite( const std::string &name = "" );
~TestComposite();
void run( TestResult *result );
int countTestCases() const;
std::string getName() const;
private:
TestComposite( const TestComposite &other );
TestComposite &operator =( const TestComposite &other );
virtual void doStartSuite( TestResult *controller );
virtual void doRunChildTests( TestResult *controller );
virtual void doEndSuite( TestResult *controller );
private:
const std::string m_name;
};
现在真相大白:原来就是一个composite模式。现在回头看CPPUNIT_TEST这个宏:
#define CPPUNIT_TEST( testMethod ) \
CPPUNIT_TEST_SUITE_ADD_TEST( \
( new CPPUNIT_NS::TestCaller<TestFixtureType>( \
context.getTestNameFor( #testMethod), \
&TestFixtureType::testMethod, \
context.makeFixture() ) ) )
这部分其实就是生成Test的,要记住TestCaller派生自TestCase,TestCase派生自TestLeaf和TestFixture,TestLeaf派生自TestLeaf;TestSuite派生自TestComposite,TestComposite派生自Test,关系挺复杂吧?其实就是一个Composite模式,画个UML图就明白了。我们该看CPPUNIT_TEST_SUITE_ADD_TEST了,这个宏简单,定义在HelpMacros.h里面:
* \param test Test to add to the suite. Must be a subclass of Test. The test name
* should have been obtained using TestNamer::getTestNameFor().
*/
#define CPPUNIT_TEST_SUITE_ADD_TEST( test ) \
context.addTest( test )
这个宏就是调用context添加test的,具体context又交给m_suite去做,前面见过了。下面接着第三个宏CPPUNIT_TEST_SUITE_END。
/*! \brief End declaration of the test suite.
*
* After this macro, member access is set to "private".
*
* \see CPPUNIT_TEST_SUITE.
* \see CPPUNIT_TEST_SUITE_REGISTRATION.
*/
#define CPPUNIT_TEST_SUITE_END() \
} \
\
static CPPUNIT_NS::TestSuite *suite() \
{ \
const CPPUNIT_NS::TestNamer &namer = getTestNamer__(); \
std::auto_ptr<CPPUNIT_NS::TestSuite> suite( \
new CPPUNIT_NS::TestSuite( namer.getFixtureName() )); \
CPPUNIT_NS::ConcretTestFixtureFactory<TestFixtureType> factory; \
CPPUNIT_NS::TestSuiteBuilderContextBase context( *suite.get(), \
namer, \
factory ); \
TestFixtureType::addTestsToSuite( context ); \
return suite.release(); \
} \
private: /* dummy typedef so that the macro can still end with ';'*/ \
typedef int CppUnitDummyTypedefForSemiColonEnding__
这个宏一开始结束了上面的addTestToSuite函数,然后开始一个新的函数suite;此函数生成一个新的TestSuite、一个TestFixtureFactory的实例,然后用生成的suite、factory和前面定义的static的namer生成一个TestSuiteBuilderContext的实例context,然后调用CPPUNIT_TEST宏里面定义的addTestToSuite函数将要测试的test加上去(生成绑定了Fixture和testMethod的TestCaller然后添加到TestSuite里面)。
现在该轮到我们略过去的TestCase的run函数:
/// Run the test and catch any exceptions that are triggered by it
void
TestCase::run( TestResult *result )
{
result->startTest(this);
if ( result->protect( TestCaseMethodFunctor( this, &TestCase::setUp ),
this,
"setUp() failed" ) )
{
result->protect( TestCaseMethodFunctor( this, &TestCase::runTest ),
this );
}
result->protect( TestCaseMethodFunctor( this, &TestCase::tearDown ),
this,
"tearDown() failed" );
result->endTest( this );
}
看TestResult类吧,我们前面知道它是用来收集测试结果的:
/*! \brief Manages TestListener.
* \ingroup TrackingTestExecution
*
* A single instance of this class is used when running the test. It is usually
* created by the test runner (TestRunner).
*
* This class shouldn't have to be inherited from. Use a TestListener
* or one of its subclasses to be informed of the ongoing tests.
* Use a Outputter to receive a test summary once it has finished
*
* TestResult supplies a template method 'setSynchronizationObject()'
* so that subclasses can provide mutual exclusion in the face of multiple
* threads. This can be useful when tests execute in one thread and
* they fill a subclass of TestResult which effects change in another
* thread. To have mutual exclusion, override setSynchronizationObject()
* and make sure that you create an instance of ExclusiveZone at the
* beginning of each method.
*
* \see Test, TestListener, TestResultCollector, Outputter.
*/
class CPPUNIT_API TestResult : protected SynchronizedObject
{
public:
/// Construct a TestResult
TestResult( SynchronizationObject *syncObject = 0 );
/// Destroys a test result
virtual ~TestResult();
virtual void addListener( TestListener *listener );
virtual void removeListener( TestListener *listener );
/// Resets the stop flag.
virtual void reset();
/// Stop testing
virtual void stop();
/// Returns whether testing should be stopped
virtual bool shouldStop() const;
/// Informs TestListener that a test will be started.
virtual void startTest( Test *test );
/*! \brief Adds an error to the list of errors.
* The passed in exception
* caused the error
*/
virtual void addError( Test *test, Exception *e );
/*! \brief Adds a failure to the list of failures. The passed in exception
* caused the failure.
*/
virtual void addFailure( Test *test, Exception *e );
/// Informs TestListener that a test was completed.
virtual void endTest( Test *test );
/// Informs TestListener that a test suite will be started.
virtual void startSuite( Test *test );
/// Informs TestListener that a test suite was completed.
virtual void endSuite( Test *test );
/*! \brief Run the specified test.
*
* Calls startTestRun(), test->run(this), and finally endTestRun().
*/
virtual void runTest( Test *test );
/*! \brief Protects a call to the specified functor.
*
* See Protector to understand how protector works. A default protector is
* always present. It captures CppUnit::Exception, std::exception and
* any other exceptions, retrieving as much as possible information about
* the exception as possible.
*
* Additional Protector can be added to the chain to support other exception
* types using pushProtector() and popProtector().
*
* \param functor Functor to call (typically a call to setUp(), runTest() or
* tearDown().
* \param test Test the functor is associated to (used for failure reporting).
* \param shortDescription Short description override for the failure message.
*/
virtual bool protect( const Functor &functor,
Test *test,
const std::string &shortDescription = std::string("") );
/// Adds the specified protector to the protector chain.
virtual void pushProtector( Protector *protector );
/// Removes the last protector from the protector chain.
virtual void popProtector();
protected:
/*! \brief Called to add a failure to the list of failures.
*/
void addFailure( const TestFailure &failure );
virtual void startTestRun( Test *test );
virtual void endTestRun( Test *test );
protected:
typedef CppUnitDeque<TestListener *> TestListeners;
TestListeners m_listeners;
ProtectorChain *m_protectorChain;
bool m_stop;
private:
TestResult( const TestResult &other );
TestResult &operator =( const TestResult &other );
};
在去查Protector、TestResult怎么作用之前,先看写了MyTest类后怎么进行测试。首先用一个宏注册(注册什么?注册到哪里?)
且听下回分解吧。