从Primer文档中了解到,一般情况下,在TEST()宏定义一个自己的测试案例,第一部分为单元测试名字,第二部分为测试名。那么TEST()宏的原定义是一个什么样的形式的呢?为什么只需要定义TEST()宏就可以了呢,这里面有什么技巧吗?作为一个技术员,虽然只需要接口就能够编写应用程序,然而如果能够获取内部更多信息,那么我们将会编写的更加完美的程序,编写的程序更加有效。那么下面我们就慢慢开始解剖TEST()宏。

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

在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()宏,该宏的实现如下(注意不要忽略了\符号):

// Helper macro for defining tests.
#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),让我们把眼睛往上面瞄一瞄,原来类名字是那么定义的:

// Expands to the name of the class that implements the given test.
#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
  test_case_name##_##test_name##_Test

##表示链接前后字符为字符串。如果我们定义的TEST(FactorialTest, Zero),将会得到一个FactorialTest_Zero_Test的类名。

在GoogleTest的Primer中宏是被这么定义的:

// Tests factorial of 0.
TEST(FactorialTest, Zero) {
  EXPECT_EQ(1, Factorial(0))
}

很少看到宏定义后面还能与函数类似的,带个括号还能够编写任何语句,通过上面的代码就清楚了,{}中的内容是成员虚拟函数的内容

TEST(FactorialTest, Zero)
{
    EXPECT_EQ(1, Factorial(0));
}

宏定义扩展将得到:

FactorialTest_Zero_Test
class FactorialTest_Zero_Test : public ::testing::Test
{
public:
         FactorialTest_Zero_Test() {}
private:
         virtual void TestBody();
         static ::testing::TestInfo* const test_Info_;
         GTEST_DISALLOW_COPY_AND_ASSIGN( FactorialTest_Zero_Test  );
};

::testing::TestInfo* const FactorialTest_Zero_Test::test_Info_ =
 ::testing::internal::MakeAndRegisterTestInfo( FactorialTest, Zero, “”, “”, \
         ::testing::internal::GetTypeId<::testing::Test>(),
         ::testing::Test::SetUpTestCase,
         ::testing::Test::TearDownTestCase,
         new ::testing::internal::TestFactoryImpl< FactorialTest_Zero_Test> );
void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
{
  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,实现的非常有技巧:

Code
template <typename T>
inline TypeId GetTypeId() {
  static bool dummy = false;
  // The compiler is required to create an instance of the static
  // variable dummy for each T used to instantiate the template.
  // Therefore, the address of dummy is guaranteed to be unique.
  return &dummy;
}

利用编译器只为模板生成一个对象实例,相同类(

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就可以控制生成的对象了!

我们通过new TestFactoryImpl来得到TestFactoryImpl的实例传递给MakeAndRegisterTestInfo函数,从而实现内部的工厂类管理。内部得到工厂类后调用其虚拟函数CreateTest方法,创建一个新的自定义对象,并管理该对象,由于自定义对象覆盖了其基类的TestBody方法,因此可以通过获得的自定义对象来调用TestBody方法,从而实现了自动测试的目的。

上面一段的内容均是猜测,是否如此,我们下回一起分析ALL_TEST_RUNS()宏,看看究竟是否如此。

TEST_F()宏的实现与TEST类类似,也在下回一起分析吧。

总结一下:

TEST()宏先扩展成GTEST_TEST()宏,然后GTEST_TEST()宏组装自定义类名字,声明(派生自::testing::Test类)以及部分实现,虚拟函数TestBody由TEST()宏扩展补充实现。在类定义过程中使用模板工厂方法,创建自定义类对象,从而使得内部可以管理自定义类,最终实现测试自动化。

其中两个技巧非常不错:

1、通过地址唯一性来获取类ID的唯一性,并且用到了编译器的特性。

template <typename T>
inline TypeId GetTypeId() {
  static bool dummy = false;
  return &dummy;
}

2

、通过覆盖基类方法,创建自定义类的实例,采用模板方法,使得实例创建更加简洁,无需更多的Factory类。

template <class TestClass>
class : public TestFactoryBase {
public:
  virtual Test* CreateTest() { return new TestClass; }
};

转载于:https://www.cnblogs.com/ubunoon/archive/2008/11/08/GoogleTestTestAnalysis.html

Gooogle Test中的TEST()宏代码分析相关推荐

  1. python3颜色代码_python3中布局背景颜色代码分析

    我们想要给某个模块着重突出,以便引起大家注意的时候,通常给那个模块的背景颜色换亮一点.这种情况的操作多见于我们想要突出表格中的某个数据,或是某一项的名称. 在python3中设置布局背景颜色需要通过代 ...

  2. python背景颜色代码大全_python3中布局背景颜色代码分析

    我们想要给某个模块着重突出,以便引起大家注意的时候,通常给那个模块的背景颜色换亮一点.这种情况的操作多见于我们想要突出表格中的某个数据,或是某一项的名称. 在python3中设置布局背景颜色需要通过代 ...

  3. vins中imu融合_VINS-Mono代码分析与总结(最终版)

    VINS-Mono代码分析总结 参考文献 前言 ??视觉与IMU融合的分类: 松耦合和紧耦合:按照是否把图像的Feature加入到状态向量区分,换句话说就是松耦合是在视觉和IMU各自求出的位姿的基础上 ...

  4. uboot中关于LCD的代码分析

    以下内容源于朱有鹏<物联网大讲坛>课程的学习,如有侵权,请告知删除. 1.在uboot-jiuding/board.c中,init_sequence中的display_banner中的op ...

  5. 利用DOCX文档远程模板注入执行宏代码

    利用DOCX文档远程模板注入执行宏代码 简介 本地文件中在没有宏代码的情况下,攻击者可以尝试执行远程文件中宏代码.其中来自APT28的最新样本将此技术展现的淋漓尽致.该样本是docx文件,文件内没有任 ...

  6. 内核RDMA模块(siw)代码分析

    siw是内核中实现的RDMA设备驱动模块.与其他RDMA设备驱动不同的是,这个模块没有对应的硬件设备,而是通过软件方式模拟了一个使用iWARP协议的RDMA设备,通过内核的socket接口完成tcp报 ...

  7. Macro_Pack中的宏代码混淆方法分析

    宏混淆工具 Macro_Pack是一个用于自动化混淆及生成Office文档.VB脚本.快捷方式等文件的工具,其主要用于渗透测试.demo以及社会工程学评估.macro_pack的目标是简化利用流程,反 ...

  8. Nature子刊:宏基因组中挖掘原核基因组的分析流程

    宏基因组中挖掘原核基因组的分析流程 从宿主相关的短读长鸟枪宏基因组测序数据中恢复原核基因组 Recovering prokaryotic genomes from host-associated, s ...

  9. linux内核中链表代码分析---list.h头文件分析(二)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...

最新文章

  1. 用matlab自己搭建bp神经网络,怎样在matlab里建立一个BP神经网络模型?
  2. java socket相关的timeout
  3. Chrome 扩展 最近的历史 HistoryBar v1.1
  4. Linxu入门(一)
  5. Spring Cloud Hystrix——熔断器
  6. libsvm3.22——在matlab(32位和64位)中的安装
  7. 光栅衍射C语言程序,基于matcom的光栅衍射仿真程序
  8. Android版式– LinearLayout,RelativeLayout
  9. linux抓包工具:tcpdump 工具用法
  10. C ++入门系列博客一 最初的起点 — Hello World
  11. if语句、逻辑表达式、运算符
  12. [黑科技] 使用Word和Excel自制题库自判断答题系统
  13. 思维导图——线性代数知识点总结
  14. ps多种去水印方法与技巧-适合各种水印
  15. 左神算法中级班第三课[C++代码]
  16. 分治法求循环赛日程表
  17. 湿度控制c语言程序,基于51单片机的湿度控制系统设计.doc
  18. [应用笔记] HTU21D(HTU20/SHT21/SHT20)温湿度传感器常见问题
  19. 性能监控之linux sar命令
  20. android 获取usb serialnumber,iOS9获取手机序列号serialNumber(UDID)

热门文章

  1. 开机启动脚本/etc/init.d/rcS
  2. BINDER SECCTX PATCH ANALYSIS
  3. 一文读懂区块链上的隐私与监管问题
  4. Android安全教程(3)---Fiddler简易使用教程之抓取https包
  5. BZOJ 2038: [2009国家集训队]小Z的袜子(莫队算法例题)
  6. android 默认开关,android默认设置的开关
  7. cadence 常见pcb电阻_经验分享|高频PCB设计中出现的干扰分析及对策
  8. python软件开发文档目录_python学习之-软件开发的目录规范
  9. 构架、框架、设计模式之间的关系简述
  10. ECCV 2012 KCF/DCF:《High-speed tracking with kernelized correlation filters》论文笔记