如下代码所示:
//定义测试用例类
class SimpleTestCase:publicCPPUNIT_NS::TestCase
{
public:
virtual voidrunTest()
{
int x = 3;
int y = 6;
CPPUNIT_ASSERT(x+y == 10);
}
};
// 创建TestCase
CPPUNIT_NS::Test*makeSingleTestCase()
{
return newSimpleTestCase();
}
如下代码所示:
//定义TestFixture类
class SimpleTestFixture:publicCPPUNIT_NS::TestFixture
{
public:
virtual voidsetUp()
{
x = 10;
y = 6;
}
virtual voidtearDown()
{
}
void TestSub()
{
CPPUNIT_ASSERT(x-y == 3);
}
protected:
int x;
int y;
};
//创建TestCaller
CPPUNIT_NS::Test*makeTestCallCase()
{
SimpleTestFixture* fixture= new SimpleTestFixture;
CPPUNIT_NS::TestCaller<SimpleTestFixture>*caller=
new CPPUNIT_NS::TestCaller<SimpleTestFixture>("SimpleTestFixture",
&SimpleTestFixture::TestSub,
fixture);
return caller;
}
如下代码所示:
//创建TestFixture类
class SimpleTestSuiteFixture:publicCPPUNIT_NS::TestFixture
{
public:
virtual voidsetUp()
{
x = 15;
y = 5;
}
virtual voidtearDown()
{
}
void TestMulti()
{
CPPUNIT_ASSERT(x*y == 65);
}
void TestDivide()
{
CPPUNIT_ASSERT(x/y == 4);
}
protected:
int x;
int y;
};
//创建并添加TestSuite
CPPUNIT_NS::Test*makeTestSuite()
{
CPPUNIT_NS::TestCaller<SimpleTestSuiteFixture>*caller1 =
new CPPUNIT_NS::TestCaller<SimpleTestSuiteFixture>("SimpleTestSuiteFixture",
&SimpleTestSuiteFixture::TestMulti,
new SimpleTestSuiteFixture());
CPPUNIT_NS::TestCaller<SimpleTestSuiteFixture>*caller2 =
new CPPUNIT_NS::TestCaller<SimpleTestSuiteFixture>("SimpleTestSuiteFixture",
&SimpleTestSuiteFixture::TestDivide,
new SimpleTestSuiteFixture());
CPPUNIT_NS::TestSuite*suite =new CPPUNIT_NS::TestSuite;
suite->addTest(caller1);
suite->addTest(caller2);
return suite;
}
#include "cppunit/extensions/HelperMacros.h"
#include "cppunit/ui/text/TestRunner.h"
#include "simple_test.h"
int _tmain(intargc, _TCHAR*argv[])
{
CppUnit::TextUi::TestRunnerrunner;
runner.addTest(makeSingleTestCase());
runner.addTest(makeTestCallCase());
runner.addTest(makeTestSuite());
runner.run();
return 0;
}
运行结果如图所示:
通过上面的例子我们会发现编写测试用例的过程较为复杂,所以CppUnit提供了一些工具类来简化定义测试用例的流程,我们把上面的例子做一些改动。首先把所有的测试用例都放到一个TestSuite,再使用工具类的新写法来完成这个测试用例。
#include "cppunit/extensions/HelperMacros.h"
class MathTest:public CPPUNIT_NS::TestFixture
{
CPPUNIT_TEST_SUITE(MathTest);//定义TestSuite
CPPUNIT_TEST(TestAdd);//添加TestCase
CPPUNIT_TEST(TestSub);//添加TestCae
CPPUNIT_TEST(TestMulti);//添加TestCase
CPPUNIT_TEST(TestDivide);//添加TestCase
CPPUNIT_TEST_SUITE_END(); //定义TestSuite结束
public:
void TestAdd();
void TestSub();
void TestMulti();
void TestDivide();
virtual voidsetUp();
virtual voidtearDown();
protected:
int x;
int y;
};
CPPUNIT_TEST_SUITE_REGISTRATION(MathTest);//将TestSuite注册到工厂,该行非常重要,一定需要
void MathTest::TestAdd()
{
CPPUNIT_ASSERT(x+y == 21);
}
void MathTest::TestSub()
{
CPPUNIT_ASSERT(x-y == 16);
}
void MathTest::TestMulti()
{
CPPUNIT_ASSERT(x*y == 100);
}
void MathTest::TestDivide()
{
CPPUNIT_ASSERT(x/y == 3);
}
void MathTest::setUp()
{
x = 15;
y = 5;
}
void MathTest::tearDown()
{
}
#include "cppunit/extensions/TestFactoryRegistry.h"
#include "cppunit/ui/text/TestRunner.h"
int main(intargc, char *argv[])
{
CppUnit::TextUi::TestRunner runner;
CppUnit::TestFactoryRegistry®istry =
CppUnit::TestFactoryRegistry::getRegistry();
runner.addTest(registry.makeTest());
runner.run();
return 0;
}
运行结果如图所示:
CppUnit的核心如图1所示:
图1 CppUnit Core 类图
主要由以下类构成:
1. Test 所有测试的基类。
2.TestSuite 表示一系列测试用例的集合,也可以包含其他TestSuite。
3.TestCase 表示一个单一的TestCase。
4.TestFixture 用于初始化和回收测试用例。
5.TestCaller 用于封装一个TestFixture成TestCase。
6.TestRunner 启动测试用例的主程序,可以通过addTest将需要运行的测试用例添加到单元测试程序中。
从上图我们可以看出,CppUnit的核心就是TestRunner运行所有添加到它里面的Test,而一个Test可以是一个具体的TestCase,也可以是一个TestSuite,非常简单。
虽然CppUnit的核心很简单,但是需要编写测试用例的代码却比较复杂,所以CppUnit提供了扩展程序以及宏定义来帮助用户更好的使用CppUnit。Extension的类图如图2所示:
图2 CppUnit Extension 类图
Extenstion主要包括以下几大部分:
TestFactory Test工厂的虚基类
TestSuiteFactory TestSuite工厂类,用于产生具体的TestSuite
TestFactoryRegistry该类有两个功能,第一个功能是作为仓库,提供接口让其他模块注册新的TestFactory。第二功能是作为TestFactory,调用注册进去的TestFactory生成Test,并且添加到一个统一的TestSuite中,并返回给调用者。
TestSuiteBuilderContextBase该类是Extension中非常重要的一个类,该类用于将某个TestFixture中声明的TestCase添加到该TestFixture对应的TestSuite中。关于该类的作用可以等到解析HelperMacro时再分析。
TestNamer一个辅助类用于定义以及生成测试的名称。
主要包括以下四个宏:
1. CPPUNIT_TEST_SUITE
2. CPPUNIT_TEST
3. CPPUNIT_TEST_SUITE_END
4. CPPUNIT_TEST_SUITE_REGISTRATION
在上面的例子中,我们已经知道了这几个宏定义如何使用,下面我们就剖析这几个宏定义如何起作用的。
CPPUNIT_TEST_SUITE实现如下:
#define CPPUNIT_TEST_SUITE(ATestFixtureType) \
public: \
typedef ATestFixtureTypeTestFixtureType; \
\
private: \
static constCPPUNIT_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 ) \
{ \
TestSuiteBuilderContextTypecontext(baseContext)
从上面我们可以看出,CPPUNIT_TEST_SUITE定义了一个接口获取TestNamer, 同时定义了一个未结尾的函数addTestsToSuite,在这个函数中只声明了一个TestSuiteBuilderContext 类型的变量context。
CPPUNIT_TEST实现如下:
#define CPPUNIT_TEST(testMethod) \
CPPUNIT_TEST_SUITE_ADD_TEST( \
( new CPPUNIT_NS::TestCaller<TestFixtureType>( \
context.getTestNameFor(#testMethod), \
&TestFixtureType::testMethod, \
context.makeFixture()) ) )
#define CPPUNIT_TEST_SUITE_ADD_TEST(test ) \
context.addTest(test )
如上所示,CPPUNIT_TEST只是调用TestCaller生成了一个TestCase,并将这个TestCase添加到CPPUNIT_TEST_SUITE声明的变量context中。
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::TestSuiteBuilderContextBasecontext( *suite.get(), \
namer, \
factory ); \
TestFixtureType::addTestsToSuite(context ); \
return suite.release(); \
} \
private: /* dummy typedefso that the macro can still end with ';'*/ \
typedef int CppUnitDummyTypedefForSemiColonEnding__
如上所示,CPPUNIT_TEST_SUITE_END首先是一个”}”用于结束addTestsToSuite函数的定义,然后定义了一个静态函数suite生成一个TestSuite对象,并且定义了TestSuiteBuilderContextBase变量,然后调用addTestsToSuite将用户使用CPPUNIT_TEST定义的Test都添加到TestSuite对象中。
CPPUNIT_TEST_SUITE_REGISTRATION实现:
#define CPPUNIT_TEST_SUITE_REGISTRATION(ATestFixtureType ) \
static CPPUNIT_NS::AutoRegisterSuite<ATestFixtureType> \
CPPUNIT_MAKE_UNIQUE_NAME(autoRegisterRegistry__)
从上可以看出该宏只定义了一个AutoRegisterSuite 的静态变量,而通过分析AutoRegisterSuite我们知道该类的作用是生成一个当前TestFixture对应的TesSuiteFactory,同时添加到TestFactoryRegistry中。同时我们分析TesSuiteFactory知道该工厂就是通过调用静态的suite接口创建一个具体的Test。
这样,整个流程就如图3所示:
图3 Helper Marco流程