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

CppUnit代码理解(2)

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

看一个很重要的宏:

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( MyTest, "alltest" );

我也不清楚,先看宏定义吧,在HelpMacros.h里面:

/** Adds the specified fixture suite to the specified registry suite.
* \ingroup CreatingTestSuite
*
* This macro declares a static variable whose construction
* causes a test suite factory to be inserted in the global registry
* suite of the specified name. The registry is available by calling
* the static function CppUnit::TestFactoryRegistry::getRegistry().
* 
* For the suite name, use a string returned by a static function rather
* than a hardcoded string. That way, you can know what are the name of
* named registry and you don't risk mistyping the registry name.
*
* \code
* // MySuites.h
* namespace MySuites {
*   std::string math() { 
*     return "Math";
*   }
* }
*
* // ComplexNumberTest.cpp
* #include "MySuites.h"
* 
* CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ComplexNumberTest, MySuites::math() );
* \endcode
*
* \param ATestFixtureType Type of the test case class.
* \param suiteName Name of the global registry suite the test suite is 
*                  registered into.
* \warning This macro should be used only once per line of code (the line
*          number is used to name a hidden static variable).
* \see CPPUNIT_TEST_SUITE_REGISTRATION
* \see CPPUNIT_REGISTRY_ADD_TO_DEFAULT
* \see CPPUNIT_REGISTRY_ADD
* \see CPPUNIT_TEST_SUITE, CppUnit::AutoRegisterSuite, 
*      CppUnit::TestFactoryRegistry..
*/
#define CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ATestFixtureType, suiteName ) \
  static CPPUNIT_NS::AutoRegisterSuite< ATestFixtureType >                   \
             CPPUNIT_MAKE_UNIQUE_NAME(autoRegisterRegistry__ )(suiteName) 

 从上面注释的参数解释我们知道要注册的是我们的测试类,被注册到一个全局的测试包中,该测试包的名字为宏里面的suiteName。宏里面有个模板类AutoRegisterSuite,看其定义:

*! \brief (Implementation) Automatically register the test suite of the specified type.
*
* You should not use this class directly. Instead, use the following macros:
* - CPPUNIT_TEST_SUITE_REGISTRATION()
* - CPPUNIT_TEST_SUITE_NAMED_REGISTRATION()
*
* This object will register the test returned by TestCaseType::suite()
* when constructed to the test registry.
*
* This object is intented to be used as a static variable.
*
*
* \param TestCaseType Type of the test case which suite is registered.
* \see CPPUNIT_TEST_SUITE_REGISTRATION, CPPUNIT_TEST_SUITE_NAMED_REGISTRATION
* \see CppUnit::TestFactoryRegistry.
*/
template<class TestCaseType>
class AutoRegisterSuite
{
public:
  /** Auto-register the suite factory in the global registry.
   */
  AutoRegisterSuite()
      : m_registry( &TestFactoryRegistry::getRegistry() )
  {
    m_registry->registerFactory( &m_factory );
  } 

  /** Auto-register the suite factory in the specified registry.
   * \param name Name of the registry.
   */
  AutoRegisterSuite( const std::string &name )
      : m_registry( &TestFactoryRegistry::getRegistry( name ) )
  {
    m_registry->registerFactory( &m_factory );
  } 

  ~AutoRegisterSuite()
  {
    if ( TestFactoryRegistry::isValid() )
      m_registry->unregisterFactory( &m_factory );
  } 

private:
  TestFactoryRegistry *m_registry;
  TestSuiteFactory<TestCaseType> m_factory;
}; 

 

看code我们可以猜测出注册是把我们的测试类的工厂类注册到一个TestFactoryRegistry对象上去了。注意到第二个构造函数的初始化列表中,调用了TestFactoryRegistry的静态函数getRegistry,看一下TestFactoryRegistry和TestSuiteFactory的代码我们看一下就更明白了:

/*! \brief Registry for TestFactory.
* \ingroup CreatingTestSuite
*
* Notes that the registry \b DON'T assumes lifetime control for any registered tests
* anymore.
*
* The <em>default</em> registry is the registry returned by getRegistry() with the 
* default name parameter value.
*
* To register tests, use the macros:
* - CPPUNIT_TEST_SUITE_REGISTRATION(): to add tests in the default registry.
* - CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(): to add tests in a named registry.
*
* Example 1: retreiving a suite that contains all the test registered with
* CPPUNIT_TEST_SUITE_REGISTRATION().
* \code
* CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
* CppUnit::TestSuite *suite = registry.makeTest();
* \endcode
*
* Example 2: retreiving a suite that contains all the test registered with
* \link CPPUNIT_TEST_SUITE_NAMED_REGISTRATION() CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ..., "Math" )\endlink.
* \code
* CppUnit::TestFactoryRegistry &mathRegistry = CppUnit::TestFactoryRegistry::getRegistry( "Math" );
* CppUnit::TestSuite *mathSuite = mathRegistry.makeTest();
* \endcode
*
* Example 3: creating a test suite hierarchy composed of unnamed registration and
* named registration:
* - All Tests
*   - tests registered with CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ..., "Graph" )
*   - tests registered with CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ..., "Math" )
*   - tests registered with CPPUNIT_TEST_SUITE_REGISTRATION
*
* \code
* CppUnit::TestSuite *rootSuite = new CppUnit::TestSuite( "All tests" );
* rootSuite->addTest( CppUnit::TestFactoryRegistry::getRegistry( "Graph" ).makeTest() );
* rootSuite->addTest( CppUnit::TestFactoryRegistry::getRegistry( "Math" ).makeTest() );
* CppUnit::TestFactoryRegistry::getRegistry().addTestToSuite( rootSuite );
* \endcode
*
* The same result can be obtained with:
* \code
* CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
* registry.addRegistry( "Graph" );
* registry.addRegistry( "Math" );
* CppUnit::TestSuite *suite = registry.makeTest();
* \endcode
*
* Since a TestFactoryRegistry is a TestFactory, the named registries can be 
* registered in the unnamed registry, creating the hierarchy links.
*
* \see TestSuiteFactory, AutoRegisterSuite
* \see CPPUNIT_TEST_SUITE_REGISTRATION, CPPUNIT_TEST_SUITE_NAMED_REGISTRATION
*/
class CPPUNIT_API TestFactoryRegistry : public TestFactory
{
public:
  /** Constructs the registry with the specified name.
   * \param name Name of the registry. It is the name of TestSuite returned by
   *             makeTest().
   */
  TestFactoryRegistry( std::string name ); 

/// Destructor.
  virtual ~TestFactoryRegistry(); 

  /** Returns a new TestSuite that contains the registered test.
   * \return A new TestSuite which contains all the test added using 
   * registerFactory(TestFactory *).
   */
  virtual Test *makeTest(); 

  /** Returns a named registry.
   *
   * If the \a name is left to its default value, then the registry that is returned is
   * the one used by CPPUNIT_TEST_SUITE_REGISTRATION(): the 'top' level registry.
   *
   * \param name Name of the registry to return.
   * \return Registry. If the registry does not exist, it is created with the
   *         specified name.
   */
  static TestFactoryRegistry &getRegistry( const std::string &name = "All Tests" ); 

  /** Adds the registered tests to the specified suite.
   * \param suite Suite the tests are added to.
   */
  void addTestToSuite( TestSuite *suite ); 

  /** Adds the specified TestFactory to the registry.
   *
   * \param factory Factory to register. 
   */
  void registerFactory( TestFactory *factory ); 

  /*! Removes the specified TestFactory from the registry.
   * 
   * The specified factory is not destroyed.
   * \param factory Factory to remove from the registry.
   * \todo Address case when trying to remove a TestRegistryFactory.
   */
  void unregisterFactory( TestFactory *factory ); 

  /*! Adds a registry to the registry.
   * 
   * Convenience method to help create test hierarchy. See TestFactoryRegistry detail
   * for examples of use. Calling this method is equivalent to:
   * \code
   * this->registerFactory( TestFactoryRegistry::getRegistry( name ) );
   * \endcode
   *
   * \param name Name of the registry to add.
   */
  void addRegistry( const std::string &name ); 

  /*! Tests if the registry is valid.
   *
   * This method should be used when unregistering test factory on static variable 
   * destruction to ensure that the registry has not been already destroyed (in 
   * that case there is no need to unregister the test factory).
   *
   * You should not concern yourself with this method unless you are writing a class
   * like AutoRegisterSuite.
   *
   * \return \c true if the specified registry has not been destroyed, 
   *         otherwise returns \c false.
   * \see AutoRegisterSuite.
   */
  static bool isValid(); 

  /** Adds the specified TestFactory with a specific name (DEPRECATED).
   * \param name Name associated to the factory.
   * \param factory Factory to register. 
   * \deprecated Use registerFactory( TestFactory *) instead.
   */
  void registerFactory( const std::string &name,
                        TestFactory *factory ); 

private:
  TestFactoryRegistry( const TestFactoryRegistry &copy );
  void operator =( const TestFactoryRegistry &copy ); 

private:
  typedef CppUnitSet<TestFactory *, std::less<TestFactory*> > Factories;
  Factories m_factories; 

  std::string m_name;
}; 

 

其中静态函数getRegistry的实现:

TestFactoryRegistry &
TestFactoryRegistry::getRegistry( const std::string &name )
{
  return *TestFactoryRegistryList::getRegistry( name );
} 

 

再看这个TestFactoryRegistryList::getRegistry的实现:

static TestFactoryRegistry *getRegistry( const std::string &name )
  {
    // If the following assertion failed, then TestFactoryRegistry::getRegistry() 
    // was called during static variable destruction without checking the registry 
    // validity beforehand using TestFactoryRegistry::isValid() beforehand.
    assert( isValid() );
    if ( !isValid() )         // release mode
      return NULL;            // => force CRASH 

    return getInstance()->getInternalRegistry( name );
  } 

static TestFactoryRegistryList *getInstance()
  {
    static TestFactoryRegistryList list;
    return &list;
  } 

TestFactoryRegistry *getInternalRegistry( const std::string &name )
{
  Registries::const_iterator foundIt = m_registries.find( name );
  if ( foundIt == m_registries.end() )
  {
    TestFactoryRegistry *factory = new TestFactoryRegistry( name );
    m_registries.insert( std::pair<const std::string, TestFactoryRegistry*>( name, factory ) );
    return factory;
  }
  return (*foundIt).second;
} 

 

看明白了额,一个singleton模式又浮出水面。

然后看下面这两个函数的实现:

 

Test *
TestFactoryRegistry::makeTest()
{
  TestSuite *suite = new TestSuite( m_name );
  addTestToSuite( suite );
  return suite;
} 

void 
TestFactoryRegistry::addTestToSuite( TestSuite *suite )
{
  for ( Factories::iterator it = m_factories.begin(); 
        it != m_factories.end(); 
        ++it )
  {
    TestFactory *factory = *it;
    suite->addTest( factory->makeTest() );
  }
} 

 

结合着下面的main函数就很明白了,先创建一个作为root的TestSuite,然后各个TestFactory生成的TestSuite再统统加到这个作为root的TestSuite里面去了。

int main()
{
    CPPUNIT_NS::TestResult controller;
    CPPUNIT_NS::TestRunner runner;
    CPPUNIT_NS::TestResultCollector result;
    controller.addListener(&result);
    runner.addTest(CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest());
    runner.run(controller, "");
} 

 

 

接着看这个controller和runner以及results直接的关系。看类TestResult类的addListener函数的定义:

/*! \brief Runs a test using the specified controller.
  * \param controller Event manager and controller used for testing
  * \param testPath Test path string. See Test::resolveTestPath() for detail.
  * \exception std::invalid_argument if no test matching \a testPath is found.
  *                                  see TestPath::TestPath( Test*, const std::string &)
  *                                  for detail.
  */
 virtual void run( TestResult &controller,
                   const std::string &testPath = "" )
{
  TestPath path = m_suite->resolveTestPath( testPath );
  Test *testToRun = path.getChildTest(); 

  controller.runTest( testToRun );
} 

 最后还是交给TestResult的runTest去指向:

 

void 
TestResult::runTest( Test *test )
{
  startTestRun( test );
  test->run( this );
  endTestRun( test );
} 

 Test case最终就这样一层层call下去了,但是前面TestResult的那个protect函数怎么起作用的呢,就是怎么监测测试的error、failure和success呢?具体函数定义如下:

*! \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("") ); 

 再看看下面这些代码我们就大彻大悟了!

TestResult::TestResult( SynchronizationObject *syncObject )
    : SynchronizedObject( syncObject )
    , m_protectorChain( new ProtectorChain() )
    , m_stop( false )
{ 
  m_protectorChain->push( new DefaultProtector() );
} 

bool 
DefaultProtector::protect( const Functor &functor,
                           const ProtectorContext &context )
{
  try
  {
    return functor();
  }
  catch ( Exception &failure )
  {
    reportFailure( context, failure );
  }
  catch ( std::exception &e )
  {
    std::string shortDescription( "uncaught exception of type " );
#if CPPUNIT_USE_TYPEINFO_NAME
    shortDescription += TypeInfoHelper::getClassName( typeid(e) );
#else
    shortDescription += "std::exception (or derived).";
#endif
    Message message( shortDescription, e.what() );
    reportError( context, message );
  }
  catch ( ... )
  {
    reportError( context,
                 Message( "uncaught exception of unknown type") );
  }
  return false;
}   

 

大致浏览完了,最好看一下一个类图关系:

  • 大小: 26.7 KB
分享到:
评论

相关推荐

    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源程序及教程

    通过学习这些例子,你可以更好地理解cppunit的使用方式,并将其应用到自己的项目中。 总结,`cppunit`是一个强大的C++单元测试框架,通过它你可以编写高质量的代码,确保软件的可靠性。通过学习和实践提供的源代码...

    CPPUNIT单元测试源代码

    在给定的压缩包文件"cppunit-1.12.1"中,我们可以找到CPPUNIT的1.12.1版本源代码。这个版本可能包含了头文件、源文件、示例、文档和其他辅助资源,使得开发者能够理解和使用CPPUNIT来编写自己的单元测试。 CPPUNIT...

    cppunit

    cppunit为C++开发者提供了一种组织和自动化测试代码的方式,确保软件组件的质量和稳定性。在本文中,我们将深入探讨cppunit的核心概念、使用方法以及如何通过它来构建和运行测试用例。 ### 核心概念 1. **测试框架...

    CppUnit源码解读.rar

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

    cppunit for vs2008

    首先,我们来理解cppunit的核心概念。cppunit提供了一组类和宏,使开发者可以方便地创建单元测试,验证代码功能的正确性。它包括测试套件、测试案例、断言等组件,帮助确保软件质量。 **编译cppunit的步骤:** 1. ...

    CppUnit 快速使用指南

    2. **创建测试类**:为待测试的代码编写测试类,继承自 CppUnit 的 `TestFixture` 类。 3. **定义测试用例**:在测试类中,定义 `void setUp()` 准备测试环境,`void tearDown()` 清理环境。然后,为每个要测试的...

    cppunit for vs2008的代码修改

    2. **链接器设置**:配置链接器选项,添加cppunit所需的库文件,并确保库的搜索路径正确。 3. **源代码适配**:如果cppunit源代码中存在与vc9不兼容的部分,需要进行适当的修改,如替换过时的函数,修正类型定义等...

    cppunit 简介及入门

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

    cppunit-1.12.1.zip

    2. **构建脚本**:如Makefile或CMakeLists.txt,用于编译和构建CppUnit库。 3. **测试示例**:可能包含测试用例,展示如何使用CppUnit编写和运行测试。 4. **文档**:README文件、用户手册或API文档,帮助理解如何...

    cppunit-1.12.0 单元测试工具

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

    cppunit 例子 vc6.0

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

    CppUnit(1.12.1)

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

Global site tag (gtag.js) - Google Analytics