Gooogle Test中的TEST()宏代码分析
2008-11-08 23:38 ubunoon 阅读(1069) 评论(1) 编辑 收藏 举报在gtest/gtest.h头文件中,可以找到TEST()宏的定义:
#define TEST(test_case_name, test_name)\
GTEST_TEST(test_case_name, test_name, ::testing::Test)
也是就是TEST()宏是通过GTEST_TEST宏来实现的,也就是TEST()宏将用GTEST_TEST()来替代。那么既然想要刨根问底,我们就要知道GTEST_TEST()是怎么实现的,我在gtest.h头文件通过查找方式怎么也找不到GTEST_TEST()宏,那么该文件在哪儿呢?
Primer并没有提示我们在外部使用GTEST_TEST(),这也就是说GTEST_TEST()应该是内部使用的,否则也不用TEST在来包装一下的。查看gtest目录后断定应该在gtest-internal.h文件中,打开gtest-internal.h文件,查找搜索了一下,嘿嘿,果真在里面有GTEST_TEST()宏,该宏的实现如下(注意不要忽略了\符号):
#define GTEST_TEST(test_case_name, test_name, parent_class)\
class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\
public:\
GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\
private:\
virtual void TestBody();\
static ::testing::TestInfo* const test_info_;\
GTEST_DISALLOW_COPY_AND_ASSIGN(\
GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\
};\
\
::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\
::test_info_ =\
::testing::internal::MakeAndRegisterTestInfo(\
#test_case_name, #test_name, "", "", \
::testing::internal::GetTypeId< parent_class >(), \
parent_class::SetUpTestCase, \
parent_class::TearDownTestCase, \
new ::testing::internal::TestFactoryImpl<\
GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\
void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
从上面可以看到GTEST_TEST申明了一个类,该类派生自parent_class,类含有一个静态成员变量指针,从TEST宏定义中知道在外部定义TEST()将获取一个从::testing::Test中派生的类申明,类名字为
GTEST_TEST_CLASS_NAME_(test_case_name, test_name),让我们把眼睛往上面瞄一瞄,原来类名字是那么定义的:
#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
test_case_name##_##test_name##_Test
##表示链接前后字符为字符串。如果我们定义的TEST(FactorialTest, Zero),将会得到一个FactorialTest_Zero_Test的类名。
在GoogleTest的Primer中宏是被这么定义的:
TEST(FactorialTest, Zero) {
EXPECT_EQ(1, Factorial(0))
}
很少看到宏定义后面还能与函数类似的,带个括号还能够编写任何语句,通过上面的代码就清楚了,{}中的内容是成员虚拟函数的内容
将
宏定义扩展将得到:
我们一步步来分析,这个类很简单,一个默认的构造函数实现,一个虚拟函数声明,一个静态变量声明,一个未知的宏GTEST_DISALLOW_COPY_AND_ASSIGN。默认构造函数就不必多说了。先来看虚拟函数,底部的扩展将虚拟函数的实现扩展出来了,前面分析说过,宏TEST()中的内容是虚拟函数中的内容,也就是我们是实现的是虚拟函数TestBody的内容。依据一般的思想,测试内部通过模板设计模式思想来调用该虚拟函数,从而达到TestBody被执行,那么内部如何知道新设计的类呢?那么就需要静态变量testInfo或未知宏来说明了,testInfo通过内部函数MakeAndRegisterTestInfo()调用来实现其指针的赋值。MakeAndRegisterTestInfo()原型如下:
TestInfo* MakeAndRegisterTestInfo(
const char* test_case_name, const char* name,
const char* test_case_comment, const char* comment,
TypeId fixture_class_id,
SetUpTestCaseFunc set_up_tc,
TearDownTestCaseFunc tear_down_tc,
TestFactoryBase* factory);
参数说明:
test_case_name: 测试案例名称
name: 测试名称
test_case_comment: 测试案例注释,将在测试输出中包含
comment: 测试注释,将在测试输出中包含
fixture_class_id: test fixture类的ID
set_up_tc: 指向构建测试案例的函数指针
tear_down_tc: 指向销毁测试案例的函数指
factory: 指向创建测试对象的工厂对象,新创建的TestInfo实例假定属于工厂对象
显然前面四个参数毋庸过多的解释,第五个参数test fixture的ID,实现的非常有技巧:
利用编译器只为模板生成一个对象实例,相同类(T)仅有一个GetTypeId()函数,而不同类的GetTypeId()函数是不同的,从而,相同的函数具有相同的dummy地址,不同的函数dummy地址不同,从而实现了类的id唯一。没错,代码很简单,但实现的非常perfect。真正应了一句,简单才是最好的!
函数地址的指针的赋值实现就相当贵简单了一些,通过传递两个静态实现函数即可,如果需要自己的函数实现,依据Primer教程实现即可!此处暂不分析这部分内容。
最后一个是工厂类,显然工厂需要能够生成自定义类,但是工厂并不知道我们定义了什么样的类?那么Google Test是如何使实现的呢,代码是最好的老师:
// This class provides implementation of TeastFactoryBase interface.该类实现了TestFactoryBase接口
// It is used in TEST and TEST_F macros. 在TEST和TEST_F宏中被实现
template <class TestClass>
class : public TestFactoryBase {
public:
virtual Test* CreateTest() { return new TestClass; }
};
没错,通过模板,新建一个自定义对象,通过虚拟函数返回一个基类的指针,这样TestFactoryBase就可以控制生成的对象了!
上面一段的内容均是猜测,是否如此,我们下回一起分析ALL_TEST_RUNS()宏,看看究竟是否如此。
TEST_F()宏的实现与TEST类类似,也在下回一起分析吧。
总结一下:
TEST()宏先扩展成GTEST_TEST()宏,然后GTEST_TEST()宏组装自定义类名字,声明(派生自::testing::Test类)以及部分实现,虚拟函数TestBody由TEST()宏扩展补充实现。在类定义过程中使用模板工厂方法,创建自定义类对象,从而使得内部可以管理自定义类,最终实现测试自动化。
其中两个技巧非常不错:
1、通过地址唯一性来获取类ID的唯一性,并且用到了编译器的特性。
inline TypeId GetTypeId() {
static bool dummy = false;
return &dummy;
}
2、通过覆盖基类方法,创建自定义类的实例,采用模板方法,使得实例创建更加简洁,无需更多的Factory类。
class : public TestFactoryBase {
public:
virtual Test* CreateTest() { return new TestClass; }
};
*
* Copyright (c) 2011 Ubunoon.
* All rights reserved.
*
* email: netubu#gmail.com replace '#' to '@'
* http://www.cnblogs.com/ubunoon
* 欢迎来邮件定制各类验证码识别,条码识别,图像处理等软件
* 推荐不错的珍珠饰品,欢迎订购 * 宜臣珍珠(淡水好珍珠) */