`
standalone
  • 浏览: 610492 次
  • 性别: 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测试框架详解** cppunit是C++编程语言中的一个单元测试框架,它借鉴了JUnit在Java世界中的设计理念,为C++开发者提供了...在使用cppunit时,理解其核心概念和使用流程,能有效地提高软件开发的可靠性和效率。

    C++ CPPUNIT 示例代码

    总的来说,这个压缩包提供了学习和实践C++单元测试的一个起点,通过分析和运行其中的代码,开发者可以深入理解CppUnit的工作原理,以及如何有效地使用它来提高代码质量。对于想要掌握C++单元测试的人来说,这是一个...

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

    CPPUNIT 是一个流行的开源C++测试框架,它用于编写单元测试,遵循测试驱动开发(TDD)的原则。...在提供的压缩包中,包含了CPPUNIT的源码、配置指南以及使用示例代码,这将有助于你更好地理解和应用这个测试框架。

    CPPUNIT 单元测试的一个源代码

    标题 "CPPUNIT 单元测试的一个源代码" 暗示了这个压缩包包含的是一个使用 C++ 测试框架 CPPUNIT 进行单元测试的实例。CPPUNIT 是一个针对 C++ 开发者的开源测试框架,类似于 Java 中的 JUnit。它允许开发者编写可...

    CPPUnit

    **正文** `CPPUnit`是C++编程语言中的一款单元测试框架,它的设计灵感来源于Java的JUnit测试框架。在软件开发过程中,单元测试是保证代码质量、...正确理解和使用`CPPUnit`,有助于提升软件项目的整体质量和稳定性。

    cppunit源程序及教程

    1. 下载cppunit的源代码包。 2. 解压并进入源代码目录。 3. 使用C++的构建工具(如`cmake`、`make`或IDE)配置和编译源代码。 4. 安装编译后的库到系统路径或者你的项目依赖目录。 ### 三、cppunit的使用 #### 1. ...

    CPPUNIT单元测试源代码

    1. 测试框架:CPPUNIT提供了一个框架,允许开发者定义和组织测试用例,然后运行这些用例以检查代码的功能。它处理测试的启动、执行和结果报告。 2. 测试套件(Test Suite):测试用例通常会被组织成测试套件,一个...

    cppunit

    1. **测试框架**:cppunit是一个测试驱动开发(TDD)的关键工具,它允许程序员编写针对代码单元的小型测试用例,以便在开发过程中尽早发现问题。 2. **测试套件(Test Suite)**:在cppunit中,测试用例通常被组织成...

    CppUnit源码解读.rar

    本文将深入探讨CppUnit的源码,帮助读者理解其内部工作原理,以便更好地利用它来编写和组织自己的测试代码。 首先,我们要明白单元测试在软件开发中的重要性。单元测试是对软件中的最小可测试单元进行检查,如函数...

    cppunit for vs2008

    1. **获取cppunit源码**:你需要从cppunit的官方网站或者其他可靠的来源下载cppunit的源代码包。 2. **配置VS2008项目**:在VS2008中新建一个空的解决方案,然后添加一个新的Win32控制台应用程序项目。选择“静态库...

    CppUnit 快速使用指南

    **CppUnit 快速使用指南** CppUnit 是一个流行的 C++ 测试框架,它使得单元测试变得简单且高效。...通过理解其基本原理,熟练掌握使用方法,以及解决实际问题,你将能够充分利用 CppUnit 进行高效的单元测试。

    cppunit for vs2008的代码修改

    1. **编译设置**:确保VS2008的项目设置与cppunit源代码的编译需求一致,例如设置正确的编译器选项,如预处理器宏定义,以及包含目录和库目录。 2. **链接器设置**:配置链接器选项,添加cppunit所需的库文件,并...

    cppunit-1.12.1.zip

    1. **源代码文件**:.cpp 和 .h 文件,分别对应C++的实现代码和接口声明。 2. **构建脚本**:如Makefile或CMakeLists.txt,用于编译和构建CppUnit库。 3. **测试示例**:可能包含测试用例,展示如何使用CppUnit编写...

    cppunit 简介及入门

    cppunit 是一个 C++ 测试框架,它使得开发者能够在 C++ 项目中编写单元测试,以确保代码的质量和稳定性。cppunit 的设计灵感来源于 JUnit,一个 Java 语言的测试框架。cppunit 提供了丰富的断言方法和测试套件管理,...

    cppunit-1.12.0 单元测试工具

    通过理解和熟练掌握cppunit,开发者可以更好地应用单元测试,从而构建更加健壮、可靠的C++应用程序。无论是初学者还是经验丰富的开发者,cppunit都是一个值得信赖的工具,能够有效地支持持续集成和持续交付的软件...

    如何使用CppUnit进行单元测试

    在深入理解CppUnit的使用方法之前,了解其核心概念是非常重要的: 1. **测试用例** (`TestCase`): 表示一组相关的测试函数。 2. **测试断言** (`Assert`): 用于验证某个条件是否满足预期,如果不满足则抛出异常。 3...

    cppunit 例子 vc6.0

    `cppunit` 是一个用于 C++ 的...通过理解和实践这些 `cppunit` 示例,开发者可以掌握如何在 C++ 项目中使用单元测试,提升代码质量和维护性。同时,了解如何在不同的开发环境中配置和使用测试框架也是一项重要的技能。

    CppUnit(1.12.1)

    **正文** CppUnit是C++编程语言中一个广泛使用的单元测试框架,它的设计灵感来源于Java的JUnit测试框架。...通过理解和熟练使用CppUnit,开发者可以更好地保障代码的健壮性和可靠性,从而提升软件工程的整体效率。

Global site tag (gtag.js) - Google Analytics