深入理解gtest:C/C++单元测试经验谈

发表于:2012-02-13来源:未知作者:admin点击数: 标签:
GoogleC++TestingFramework(简称gtest,http://code。google。com/p/googletest/)是Google公司发布的一个开源C/C++单元测试框架,已被应用于多个开源项目及Google内部项目中,知名的例子包括ChromeWeb浏览器、LLVM编译器架构、ProtocolBuffers数据交换格式及

  GoogleC++TestingFramework(简称gtest,http://code。google。com/p/googletest/)是Google公司发布的一个开源C/C++单元测试框架,已被应用于多个开源项目及Google内部项目中,知名的例子包括ChromeWeb浏览器、LLVM编译器架构、ProtocolBuffers数据交换格式及工具等。

  优秀的C/C++单元测试框架并不算少,相比之下gtest仍具有明显优势。与CppUnit比,gtest需要使用的头文件和函数宏更集中,并支持测试用例的自动注册。与CxxUnit比,gtest不要求Python等外部工具的存在。与Boost。Test比,gtest更简洁容易上手,实用性也并不逊色。Wikipedia给出了各种编程语言的单元测试框架列表(http://en。wikipedia。org/wiki/List_of_unit_testing_frameworks)。

  一、基本用法

  gtest当前的版本是1。5。0,如果使用VisualC++编译,要求编译器版本不低于7。1(VisualC++2003)。如下图所示,它的msvc文件夹包含VisualC++工程和项目文件,samples文件夹包含10个使用范例。

  一般情况下,我们的单元测试代码只需要包含头文件gtest。h。gtest中常用的所有结构体、类、函数、常量等,都通过命名空间testing访问,不过gtest已经把最简单常用的单元测试功能包装成了一些带参数宏,因此在简单的测试中常常可以忽略命名空间的存在。

  按照gtest的叫法,宏TEST为特定的测试用例(TestCase)定义了一个可执行的测试(Test)。它接受用户指定的测试用例名(一般取被测对象名)和测试名作为参数,并划出了一个作用域供填充测试宏语句和普通的C++代码。一系列TEST的集合就构成一个简单的测试程序。

  常用的测试宏如下表所示。以ASSERT_开头和以EXPECT_开头的宏的区别是,前者在测试失败时会给出报告并立即终止测试程序,后者在报告后继续执行测试程序。

  写个简单的测试试一下。假设我们实现了一个加法函数:

  对应的单元测试程序可以这样写:

  代码中,测试用例Add包含两个测试,正数和负数(这里利用了VisualC++2005以上允许标识符包含Unicode字符的特性)。编译运行效果如下:

  在控制台界面中,通过的测试用绿色表示,失败的测试用红色表示。双横线分隔了不同的测试用例,其中包含的每个测试的启动与结果用单横线和RUN。。。OK或RUN。。。FAILED标出。失败的测试会打印出代码行和原因,测试程序最后为所有用例和测试显示统计结果。建议读者试一下换成ASSERT_宏的不同之处。

  每个测试宏还可以使用<<运算符在测试失败时输出自定义信息,如:

  编译命令行中,gtest_mt。lib和gtest_main_mt。lib就是前面使用VC项目文件生成的静态库。有意思的是,测试代码不需要注册测试用例,也不需要定义main函数,这是gtest通过后一个静态库自动完成的,它的实现代码如下:

  其中,函数InitGoogleTest负责注册需要运行的所有测试用例,宏RUN_ALL_TEST负责执行所有测试,如果全部成功则返回0,否则返回1。当然,我们也可以仅链接gtest_mt。lib,自己提供main函数。

  二、测试固件

  很多时候,我们想在不同的测试执行前创建相同的配置环境,在测试执行结束后执行相应的清理工作,测试固件(TestFixture)为这种需求提供了方便。“Fixture”是一个汉语中不易直接对应的词,《美国传统词典》对它的解释是“(作为附属物的)固定装置;被固定的状态”。在单元测试中,Fixture的作用是为测试创建辅助性的上下文环境,实现测试的初始化和终结与测试过程本身的分离,便于不同测试使用相同代码来搭建固定的配置环境。用体操比赛的说法,测试过程体现了特定测试的自选动作,测试固件则体现了对一系列测试(在开始和结束时)的规定动作。有些讲单元测试的书籍直接把测试固件称为Scaffolding(脚手架)。

  使用测试固件比单纯调用TEST宏稍微麻烦一些:

  1、从gtest的testing::Test类派生一个类,用public或protected定义以下所有成员。

  2、(可选)建立环境:使用默认构造函数,或定义一个虚成员函数virtualvoidSetUp()。

  3、(可选)销毁环境:使用析构函数,或定义一个虚成员函数virtualvoidTearDown()。

  4、用TEST_F定义测试,写法与TEST相同,但测试用例名必须为上面定义的类名。

  每个带固件的测试的执行顺序是:

  1、调用默认构造函数创建一个新的带固件对象。

  2、立即调用SetUp函数。

  3、运行TEST_F体。

  4、立即调用TearDown函数。

  5、调用析构函数销毁类对象。

  从gtest的实现代码可以看到,TEST_F又从用户定义的类自动派生了一个类,因此要求public或protected的访问权限;大括号里的内容被扩展成一个名为TestBody的虚成员函数的函数体,因此可以在其中直接访问成员变量和成员函数。其实TEST也采用了相同的实现机制,只是它直接从gtest的testing::Test自动派生类,所以可以指定任意用例名。testing::Test类的SetUp和TearDown都是空函数,所以它只执行测试步骤,没有环境的创建和销毁。

原文转自:http://www.ltesting.net