`
standalone
  • 浏览: 597995 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

CppUnit代码理解(1)

    博客分类:
  • c++
阅读更多

目前网上搜索到的讲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类后怎么进行测试。首先用一个宏注册(注册什么?注册到哪里?)
且听下回分解吧。

分享到:
评论

相关推荐

    cppunit test 测试源代码

    cppUnit 单元测试 , VS2010 ~ VS 2015 都可以正常编译成功。这是新版。

    C++ CPPUNIT 示例代码

    C++ cppunit单元测试示例代码,一个比较简单的工程,主要说明cppunit如何使用

    CPPUNIT源码+配置及入门+使用示例代码.rar

    CPPUNIT 一个基于测试驱动开发的C++测试框架。 压缩包里包括了CPPUNIT源码+配置及入门文件+使用示例代码。

    CPPUnit

    CPPUnit

    CppUnit入门代码

    CppUnit 入门的代码,仅供参考。编译时请指定编译版本为 Debug|x86

    CppUnit的改进与使用

    这个改进后的使用方法是我为一个企业培训时所准备的,由于学员以...另外,为CppUnit添加了一些代码,用于解决桩代码与用例的匹配问题。这个资源包含CppUnit已编译后的库和源代码,示例工程、示例测试工程、使用说明。

    CppUnit_入门指南

    什么方法可以让你确信自己的代码正在按照自己期望的方式运行呢?有很多 种方法,使用调试器步进或在代码中添加输出文本信息的调用是其中两种比较简 单的方法,但是这两种方法都存在缺点。步进代码是个好注意,但是其...

    CppUnit源码解读.rar

    CppUnit源码解读

    CPPUNIT 单元测试的一个源代码

    NULL 博文链接:https://poson.iteye.com/blog/417485

    cppunit测试

    CppUnit[1]是Micheal Feathers由JUnit移植过来的一个在GNU LGPL条约下的并在sourcefogre网站上开源的C++单元测试框架。

    CPPunit测试应用程序源码

    自己实现的一个CPPUNIT测试程序,运行环境Win7操作系统+VC6.0.关于CPPUNIT如何配置,大家可以自己上网查询。

    CPPUNIT单元测试源代码

    CPPUNIT单元测试源代码,VC6,VC7(VS2005),VC8(VS2008)

    cppunit

    cppunit可用于单元测试

    cppunit 例子 vc6.0

    cppunit 例子 vc6.0

    CppUnit使用指南

    在 CppUnit 中,一个或一组测试用例的测试对象被称为 Fixture(设施,下文为方便理解尽量使用英文名称)。Fixture 就是被测试的目标,可能是一个对象或者一组相关的对象,甚至一个函数。 有了被测试的 fixture,就...

    cppunit在VS2008下使用

    软件测试驱动开发(TDD)是以测试作为开发过程的中心,它坚持,在编写实际代码之前,先写好基于产品代码的测试代码。开发过程的目标就是首先使测试能够通过,然后再优化设计结构。测试驱动开发式是极限编程的重要...

    CppUnit详细使用文档

    详细的CppUnit使用文档,适合使用cppunit做测试的测试人员

    CPPUNIT使用说明与环境配置

    CPPUNIT使用说明与环境配置 CPPUNIT使用说明与环境配置 CPPUNIT使用说明与环境配置 CPPUNIT使用说明与环境配置 CPPUNIT使用说明与环境配置

    CppUnit 快速使用指南

    从基本原理,CppUnit 原理,手动使用步骤,通常使用步骤,其他实际问题等方面进行讨论。以下讨论基于 CppUnit1.8.0。

    cppunit-1.12.1.zip

    CppUnit测试是软件开发过程中必不可少的一个部分,是软件质量的保障的重要手段. 单元测试作为代码级最小的单位的测试,在软件开发过程中起着举足轻重的作用。

Global site tag (gtag.js) - Google Analytics