首先约定一些常用词语。
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 ®istry = CppUnit::TestFactoryRegistry::getRegistry();
runner.addTest(registry.makeTest());
这两句导致图形界面下按"browse"按钮,会呈现一个树形结构,根节点是"All Tests",展开后是很多个TestSuite,再展开TestSuite就是很多个测试用例了。
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 ®istry = CppUnit::TestFactoryRegistry::getRegistry();
runner.addTest(registry.makeTest());
这两句导致图形界面下按"browse"按钮,会呈现一个树形结构,根节点是"All Tests",展开后是很多个TestSuite,再展开TestSuite就是很多个测试用例了。
1471

被折叠的 条评论
为什么被折叠?



