`
standalone
  • 浏览: 597988 次
  • 性别: 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 单元测试 , 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测试程序,运行环境Win7操作系统+VC6.0.关于CPPUNIT如何配置,大家可以自己上网查询。

    CPPUNIT单元测试源代码

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

    cppunit测试

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

    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