cppunit与工厂模式

首先约定一些常用词语。
        class StringToolsTest : public CPPUNIT_NS::TestFixture
        {
              CPPUNIT_TEST_SUITE( StringToolsTest );
                     CPPUNIT_TEST( testToStringInt );
                     CPPUNIT_TEST( testToStringDouble );
                     .................................
              CPPUNIT_TEST_SUITE_END();
        public:
              void testToStringInt();
              void testToStringDouble();
        }
        我们将StringToolsTest称为TestSuite或者测试套件,testToStringInt和testToStringDouble称为测试接口。测试文件又分成两部分,即测试用例头文件和测试用例实现文件,所以将StringToolsTest.h称为测试用例头文件,StringToolsTest.cpp称为测试用例实现文件。

        好了,约定好这些之后,先理解下cppunit主要几个宏的意思。
=====================================================
CPPUNIT_TEST_SUITE宏
=====================================================
#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宏
========================================================
#define CPPUNIT_TEST( testMethod )                        \
    CPPUNIT_TEST_SUITE_ADD_TEST(                           \
        ( new CPPUNIT_NS::TestCaller<TestFixtureType>(    \
                  context.getTestNameFor( #testMethod),   \
                  &TestFixtureType::testMethod,           \
                  context.makeFixture() ) ) )
==========================================
CPPUNIT_TEST_SUITE_ADD_TEST宏
==========================================
#define CPPUNIT_TEST_SUITE_ADD_TEST( test ) \
      context.addTest( test )

==========================================
CPPUNIT_TEST_SUITE_END宏
==========================================
#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__

        这三个宏的意图是很简单明了的,它实现了三个类函数,getTestNamer__函数,addTestsToSuite函数和suite函数,其中suite函数会调用到getTestNamer__函数和addTestsToSuite函数,这两个函数也仅在此处被suite调用。这个三个函数都是静态的类函数,也就是说我们设计的test都会有这三个函数。CPPUNIT_NS::TestSuiteBuilderContextBase类将几个东西揉合在一起,这里用到了Builder模式,不过源代码中并未明显地提出Builder Pattern,并且这里确实也不象是Builder  Pattern经典定义地那样使用,看来设计模式是要活学或用的呀。生成的CPPUNIT_NS::TestSuiteBuilderContextBase对象就是TestSuite对象构造的context了,它存在的目的就是要将传递给它的TestSuite对象填充好。接下来调用TestFixtureType::addTestsToSuite函数,addTestsToSuite函数所作的就是构造一个TestCaller对象,然后将其添加到context中,即“context.addTest( test )”,这个addTest函数只是简单地将test放入suite对象中。可以仔细看下这个TestCaller对象是怎么new出来的,它有三个参数,第一个是测试接口名称,第二个是测试借口的函数指针,第三个是测试suite对象,由此可以看出,cppunit为每个测试接口分别生成了一个suite对象。suite函数最后一句"return suite.release()",就是返回构造好的suite对象了,这里用到了智能指针。
       花了这么多功夫讲这个suite函数,那么它在什么地方会被调用呢?接着研究下。
       每个测试用例实现文件开始处都会用到CPPUNIT_TEST_SUITE_REGISTRATION这个宏,这个宏展开代码如下:
#define CPPUNIT_TEST_SUITE_REGISTRATION( ATestFixtureType )      \
  static CPPUNIT_NS::AutoRegisterSuite< ATestFixtureType >       \
             CPPUNIT_MAKE_UNIQUE_NAME(autoRegisterRegistry__ )
        这里的意图是很明了的,它实例了一个CPPUNIT_NS::AutoRegisterSuite静态对象,CPPUNIT_NS::AutoRegisterSuite是一个模板类,所以它需要一个实例化参数。这个静态对象的名称是CPPUNIT_MAKE_UNIQUE_NAME(autoRegisterRegistry__ ),它是怎么整的,我也不是很明白,但是这个对象在别的地方是没有被用到的,它所做的关键工作是在它的构造函数中,里面肯定操作了某些全局的东西,要不然这个对象就会在别的地方被调用到。那接下来我们来看一下AutoRegisterSuite代码,因为它是一个模板类,所以它的声明和实现文件都在AutoRegisterSuite.h这个文件中。我们用到的是AutoRegisterSuite的默认构造函数,它的代码如下:
  AutoRegisterSuite()
      : m_registry( &TestFactoryRegistry::getRegistry() )
  {
    m_registry->registerFactory( &m_factory );
  }
       这里看起来是在将一个m_factory对象添加到m_registry对象中。这个m_factory同样也是AutoRegisterSuite类的成员属性:
private:
  TestFactoryRegistry *m_registry;
  TestSuiteFactory<TestCaseType> m_factory;
        但是它却没有被显示初始化过,所以它是用自己的默认构造函数构造的,TestSuiteFactory的实现是非常简单的:
  template<class TestCaseType>
  class TestSuiteFactory : public TestFactory
  {
  public:
    virtual Test *makeTest()
    {
      return TestCaseType::suite();
    }
  };
        这里就和前面说的suite函数扯上关系了。现在理一理关系吧。
        TestFactoryRegistry.cpp中声明实现了一个TestFactoryRegistryList类,TestFactoryRegistry的所有操作几乎都是调用TestFactoryRegistryList来实现具体的操作,TestFactoryRegistryList类维护了
private:
  typedef CppUnitMap<std::string, TestFactoryRegistry *, std::less<std::string> > Registries;
  Registries m_registries;
这样一个map容器,工厂的名字为key,工厂的地址为value,使用根据工厂的名字从小到大为map内的红黑树节点排序。前面AutoRegisterSuite的构造函数中有这样一句:
      : m_registry( &TestFactoryRegistry::getRegistry() )
TestFactoryRegistry::getRegistry()是这样实现的:
TestFactoryRegistry &
TestFactoryRegistry::getRegistry( const std::string &name )
{
  return *TestFactoryRegistryList::getRegistry( name );
}
       这个name默认值为"All Tests",TestFactoryRegistryList::getRegistry如果找不到这样一个工厂,它就会new出这样一个工厂对象来,然后返回新new出来的这个工厂地址。所以,如果我们有多个TestSuite话,每个测试用例实现文件中都会调用CPPUNIT_TEST_SUITE_REGISTRATION来注册自己的工厂,那么在名称为"All Tests"的这个TestFactoryRegistry就会有好多个工厂被添加到其维护的m_factories容器中。如果使用CPPUNIT_TEST_SUITE_NAMED_REGISTRATION这个宏,那么就可以增加一个新的TestFactoryRegistry对象。在开始测试的时候,通常我们会有这样一句代码:
runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() );
这句代码就将前面所有的东西都关联起来了。
       CPPUNIT_NS::TestFactoryRegistry::getRegistry()获得名称为"All Tests"的TestFactoryRegistry对象,然后调用它的makeTest方法,该方法会调用suite函数。TestRunner有一个“WrappingSuite *m_suite”属性,这个WrappingSuite也派生于public TestSuite,现在再理一理关系吧。
       cppunit运行起来之后,首先会有一个WrappingSuite这样个TestSuite对象被生成,然后调用CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest()生成所有的TestSuite对象,并将这些添加到WrappingSuite对象中,每个接口都会被封装成一个TestCaller对象中,这些TestCaller对象又被添加到相应的TestSuite中。
        如果使用图形界面,   
CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
runner.addTest(registry.makeTest());
       这两句导致图形界面下按"browse"按钮,会呈现一个树形结构,根节点是"All Tests",展开后是很多个TestSuite,再展开TestSuite就是很多个测试用例了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值