gTest提供了很多功能方便了单元测试,但是有时候我们需要将测试代码和测试数据分离,便于维护测试数据。或者需要用多组用例对同一个测试代码测试,尽管可以用gtest的参数化的方法达到一些效果,但是参数化最多只能提供50组用例,数量有限制,且客户端为每一个参数需要写大量重复代码,服用了极低。所以,有些情况我们需要打造自己的测试框架。为了方便起见(其实也是我偷懒),假定读者已经对GTEST源码有初步了解,这里直接截取相关片段,不做详细代码解读。
首先研究gtest源码
::testing::internal::MakeAndRegisterTestInfo( \
#test_suite_name, #test_name, nullptr, nullptr, \
::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \
::testing::internal::SuiteApiResolver< \
parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__), \
::testing::internal::SuiteApiResolver< \
parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__), \
new ::testing::internal::TestFactoryImpl<GTEST_TEST_CLASS_NAME_( \
test_suite_name, test_name)>);
这是将测试单元名称等地信息进行注册。这里最后一个参数,我们可以传递一个继承父类的自定义子类,这样就可以实现将我们需要的参数都传递进去。比如,最后一个参数我们可以改成如下: new ZcGtestFactory(mytestInfo), mytestInfo是testInfo类型,ZCGtestFactory定义为:
class ZcGtestFactory : public ::testing::internal::TestFactoryBase{
public:
ZcGtestFactory(testInfo& v):_testRegInfo(v){}
virtual ::testing::Test* CreateTest(){
context ctx;
//some code...
return _testRegInfo.creator(ctx);
}
private:
testInfo _testRegInfo;
}
context定义为:
struct context{
string testDesc;
string inputFilePath; //输入参数的文件的路径
string outputFilePath; //输出参数的文件的路径
}
这里的testInfo就是我们自定义的参数结构,可以包含诸如用例ID,模块名等信息。我们这里定义为:
typedef ::testing::Test* (*TestCreator)(context&);
struct testInfo
{
std::string module;
unsigned int testNo;
std::string testDesc;
bool isSelected;
TestCreattor creator;
};
creator就是 Test* (*)(context&) 类型的函数指针,我们用这种结构的函数初始化。由于所有测试单元函数都是这种结构,所以可以写一个模板类,模板参数为测试函数。如下:
typedef void (*TestBodyFunc)(context&)
template<TestBodyFunc testFunction>
class ZcTest : public ::testing::Test
{
public:
ZcTest(context& ctx):_ctx(ctx){}
void void TestBody(){
testFunction(_ctx);
}
static ::testing::Test* ZcTest_Create(context& ctx)
{
return new ZcTest<testFunction>(ctx);
}
private:
context _ctx;
}
假设测试单元函数为:
void TestA(context& ctx)
{
//some code
}
则可以按如下构造testInfo
testInfo myTestInfo;
myTestInfo.creator = ZcTest<TestA>::ZcTest_Create;
testInfo有了,则开头那里注册的地方就可以替换为:
::testing::internal::MakeAndRegisterTestInfo(
testRegInfo.testName.c_str(),
("Test" + std::to_string(v)).c_str(),
nullptr,
nullptr,
::testing::internal::CodeLocation(__FILE__, __LINE__),
::testing::internal::GetTestTypeId(),
::testing::Test::SetUpTestCase,
::testing::Test::TearDownTestCase,
new ZcGtestFactory(myTestInfo));
这样,我们把自定义的包含测试用例号等信息地testInfo成功地加入到注册函数中,这样,测试用例数据我们可以保存在文件当中,成功地将测试单元代码和测试用例数据分离。 可以将上述代码进一步封装,达到模块解耦地效果,目的都是将测试单元代码和用例数据分离,这里不在赘述。