开源C++单元测试框架Google Test介绍

开源C++单元测试框架Google Test介绍
Google Test
Google test是针对c/c++的开源测试项目。采用的协议是BSD license,有很多著名的开源项目采用了它,包括Chromium(谷歌浏览器开发版)。

安装配置
下载主页:
http://code.google.com/p/googletest/
官方资料文档:
http://code.google.com/p/googletest/wiki/GoogleTestPrimer
http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide
当前的最新版本是1.5。包含3种tar.bz2,tar.gz和zip格式。解压后的目录结构:

其中的msvc就是VS的工程目录,可以直接打开进行编译(vs2008则需要进行工程升级转化),生成相应的lib静态库文件。在vs中需要在工程中设置3个地方,和ACE的设置一样:
1.设置gtest的头文件

注:如果测试代码需要上库,附加包含目录建议设置为相对路径。
2.设置gtest的lib文件

注:如果测试代码需要上库,附加依赖项建议不要带绝对路径。

3.设置运行时的多线程库支持

如果是Release版本,Runtime Library设为/MT。当然,其实你也可以选择动态链接(/MD),前提是你之前编译的gtest也使用了同样是/MD选项。

如果是在Linux下,就比较方便,和普通的开源软件一样,采用
1…/configure –prefix=/your install path (如果不带参数默认为/usr/local下面)
2.make
3.make install
然后就可以在工程中进行使用(如果指定了安装目录,则需要-I和-L来指明,同时也在最后的link加上-lpthread –lgtest)
下面是一个简易的写法:

By the way: 我在192.168.100.119上采用的是默认安装,所以直接加上-lgtest和-lpthread就可以了

简单例子
如果需要使用gtest,则需要包含
#include “gtest/gtest.h”
下面是一个简单例子:

编译运行的结果:

下面来依次解释:
myadd是待测试函数名,TEST是作为gTest的一次测试(其实它是由gTest包装过的一个宏),第一个参数Test_myadd是测试用例名,第二个参数IsReturnAdd是测试名(这两个参数都是自己任意定义的)。在随后的测试结果中将以“测试用例名.测试名”的形式呈现

EXPECT_EQ用于测试两个数据是否相等。第一个参数可以是你提前预定义的数据,第二个为测试函数名。
main主函数中:

testing::InitGoogleTest用来处理程序的命令行参数。RUN_ALL_TEST也是一个宏,用来运行所有的测试用例(本例中就只有一个TEST)。测试结果英文也很清晰,我就不画蛇添足了。
最后再补充一点,编译后的二进制文件支持gtest的命令行参数,可以将数据直接转化为xml

断言
gtest采用了大量的宏来包装断言,此断言不同于c语言的断言(assert),按照使用方法分为2类:
1.ASSERT系列(ASSERT_*系列的断言,当检查点失败时,退出当前函数,并非退出当前案例);
2.EXPECT系列(EXPECT_*系列的断言,当检查点失败时,继续往下运行)

按照常用功能依次分为12类,平常主要用到的是以下几类:
1.布尔值比较
2.数值型数据比较
3.字符串比较
4.浮点数比较
5.近似数比较
6.异常检测
7.自定义格式函数与参数检查

布尔值比较
ASSERT_TRUE(condition) EXPECT_TRUE(condition) condition == true
ASSERT_FALSE(condition) EXPECT_FALSE(condition) condition == false

数值型数据比较
ASSERT_EQ(expected, actual) EXPECT_EQ(expected, actual) expected == actual
ASSERT_NE(val1, val2) EXPECT_NE(val1, val2) val1 != val2
ASSERT_LT(val1, val2) EXPECT_LT(val1, val2) val1 < val2
ASSERT_LE(val1, val2) EXPECT_LE(val1, val2) val1 <= val2
ASSERT_GT(val1, val2) EXPECT_GT(val1, val2) val1 > val2
ASSERT_GE(val1, val2) EXPECT_GE(val1, val2) val2 >= val2

字符串比较
ASSERT_STREQ(str1, str2) EXPECT_STREQ(str1, str2) 两个C字符串内容相同(同时支持char *和wchar_t *类型)
ASSERT_STRNE(str1, str2) EXPECT_STRNE(str1, str2) 两个C字符串内容不同(同时支持char *和wchar_t *类型)
ASSERT_STRCASEEQ(str1,str2) EXPECT_STRCASEEQ(str1,str2) 两个C字符串内容相同,忽略大小写(只支持char *类型)
ASSERT_STRCASENE(str1,str2) EXPECT_STRCASENE(str1,str2) 两个C字符串内容不同,忽略大小写(只支持char *类型)

浮点数比较
ASSERT_FLOAT_EQ(val1,val2) EXPECT_FLOAT_EQ(val1,val2) the two float values are almost equal
ASSERT_DOUBLE_EQ(val1,val2) EXPECT_DOUBLE_EQ(val1,val2) the two double values are almost equal

近似数比较
ASSERT_NEAR(val1, val2, abs_error) EXPECT_NEAR(val1, val2, abs_error) 两个数值val1和val2的绝对值差不超过abs_error

异常检查
ASSERT_THROW(statement, exception_type) EXPECT_THROW(statement, exception_type) 抛出指定类型异常
ASSERT_THROW(statement) EXPECT_THROW(statement) 抛出任意类型异常
ASSERT_NO_THROW(statement) EXPECT_NO_THROW(statement) 不抛异常

函数值与参数检查(目前最多只支持5个参数)
ASSERT_PRED1(pred1, val1) EXPECT_PRED1(pred1, val1) pred1(val1) returns true
ASSERT_PRED2(pred2, val1, val2) EXPECT_PRED2(pred2, val1, val2) pred2(val1, val2) returns true

Windows HRESULT
ASSERT_HRESULT_SUCCEEDED
(expression) EXPECT_HRESULT_SUCCEEDED
(expression) expression is a success HRESULT
ASSERT_HRESULT_FAILED
(expression) EXPECT_HRESULT_FAILED
(expression) expression is a failure HRESULT

自定义格式函数与参数检查(目前最多支持5个参数)
ASSERT_PRED_FORMAT1(pred1, val1) EXPECT_PRED_FORMAT1(pred1, val1) pred1(val1) is successful
ASSERT_PRED_FORMAT1(pred1, val1, val2) EXPECT_PRED_FORMAT1(pred1, val1, val2) pred2(val1, val2) is successful

下面将用一个实例来演示:
我们编写了一个Configure的class,提供了3个对外的接口方法:
1.get_size(void)
2.add_item(string str)
3.get_item(int index)
现在需要对其进行测试,那么就应该依次有这3个文件:
1.Configure.h
2.Configure.cpp
3.main.cpp
首先是Configure.h:

接着是Configure.cpp:

最后是主函数调用:

进行编译后的执行:

事件机制
Gtest提供了多种事件机制方便在测试用例之前或者完成以后进行一些操作,按照使用方法分为3类:
1.全局事件:在所有测试用例执行之前和完成之后生效。可以在全局事件中完成一些测试环境的初始化和资源回收工作,比如预留内存申请/回收,组件对象初始化/析构等。
2.TestSuite级别:在指定的测试套第一个测试用例之前,最后一个测试用例之后。如果根据子模块定义测试套,那么就可以在TestSuite事件中完成一些子模块的线程、消息队列等的初始化和资源回收工作。
3.TestCase级别:在每个测试用例执行前后,即在每个测试代码的断言前后进行执行。

以下分别是3类事件的用法:
全局事件:必须通过继承testing::Environment类,实现里面的SetUp和TearDown方法:
1.SetUp()方法在所有用例执行前执行;
2.TearDown()方法在所有用例执行后执行;

完成继承类方法实现以后,还需要告诉gtest添加全局事件,我们需要在main函数中通过testing::AddGlobalTestEnvironment方法添加该全局事件。如果需要增加全局事件,也可以写多个继承类,然后将事件都添加到测试用例之前。

以下是运行全局事件以后的显示结果:

TestSuite事件:需要通过继承testing::Test类,实现里面的SetUpTestCase和TearDownTestCase两个静态方法:
1.SetUpTestCase()方法在TestSuit的第一个TestCase之前执行;
2.TearDownTestCase()方法在TestSuite的最后一个TestCase之后执行;

在编写测试用例时,需要使用TEST_F宏,第一个参数必须是上面的继承类名字,代表一个TestSuite。

以下是运行TestSuite事件以后的显示结果:

TestCase事件:与TestSuite事件实现方法相同,需要通过继承testing::Test类,但是只需要实现里面的SetUp和TearDown两个方法:
1.SetUp()方法在每个TestCase之前执行;
2.TearDown()方法在每个TestCase之后执行;

在编写测试用例时,需要使用TEST_F宏,第一个参数必须是上面的继承类名字,代表一个TestSuite,并且在测试套中添加测试用例。

以下是运行TestSuite事件以后的显示结果:

参数化
在设计测试案例时,经常需要考虑给被测函数传入不同的值的情况,以前的做法一般是写一个通用方法,然后编写在测试案例调用它,即使使用了通用方法,也需要很多重复性的工作。以下是一般的测试方法,如果需要测试N个数字,则需要拷贝复制粘贴N次:

gTest在这里提供了一个灵活的函数参数化测试的方案:
1.告诉gtest参数类型:必须添加一个继承类testing::TestWithParam,其中T就是需要参数化的参数类型。以上面为例,需要参数化一个int型的参数;

2.参数类型确定以后,需要使用一个新的宏TSET_P进行测试用例,在TEST_P宏里,使用GetParam()方法获取当前的参数的具体值;

3.使用INSTANTIATE_TEST_CASE_P宏来确定测试的参数取值范围;

其中:第一个参数是测试案例的前缀,可以任意取;第二个参数是测试案例的名称,需要和之前定义的参数化的类的名称相同,如:ParameterTest ;第三个参数是可以理解为参数生成器,上面的例子使用testing::Values表示使用括号内的参数。
Google提供了以下一系列的参数生成的函数:
Range(begin, end[, step]) 范围在begin和end之间,步长为step,不包括end边界
Values(v1, v2, …, vN) 从v1, v2到vN的值
ValuesIn(container),
ValuesIn(begin, end) 从一个C类型的数组或者STL容器或者迭代器中取值
Bool() 取false和true两个值
Combine(g1, g2, …, gN) 将g1, g2, …, gN进行排列组合,g1, g2, …, gN本身是一个参数生成器,每次分别从g1, g2, …, gN中各取出一个值,组合成一个元组(Tuple)作为一个参数
说明:这个功能只在提供了头的系统中有效。gtest会自动去判断是否支持tr/tuple,如果系统确实支持,而gtest判断错误的话,可以重新定义宏GTEST_HAS_TR1_TUPLE = 1。

以下是执行函数参数化用例以后的显示结果,最终输出测试结果命名规则为:

ParameterTrueReturn/ParameterTest.TestParamName2/ValuesIndex

除了测试用例可以参数化以外,gtest还提供了针对各种不同类型数据时的方案,以及参数化类型的方案。下面以gtest的例子为例进行介绍:
1.首先需要定义一个模版类,从testing::Test类继承;

2.然后再定义需要测试的具体数据类型,比如下面定义了测试char, int和unsigned int类型;

3.以下是执行类型参数化用例以后的显示结果;

我们在这里使用函数参数化或者类型参数化,基本上就可以满足正常单元测试需要。
死亡测试
“死亡测试”名字比较恐怖,这里的“死亡”是指程序的崩溃。通常在测试过程中,我们需要考虑各种各样的输入,有的输入可能直接导致程序崩溃,这时我们就需要检查程序是否按照预期的方式挂掉,这也就是所谓的“死亡测试”。gtest的死亡测试能做到在一个安全的环境下执行崩溃的测试案例,同时又对崩溃结果进行验证。
死亡测试宏定义
ASSERT_DEATH(statement, regex) EXPECT_DEATH(statement, regex) statement crashes with the given error
ASSERT_EXIT(statement, predicate, regex) EXPECT_EXIT(statement, predicate, regex) statement exits with the given error and its exit code matches predicate
由于有些异常只在Debug下抛出,因此还提供了*_DEBUG_DEATH,用来处理Debug和Realease下的不同。
简单来说,通过*_DEATH(statement, regex)和*_EXIT(statement, predicate, regex),我们可以非常方便的编写导致崩溃的测试案例,并且在不影响其他案例执行的情况下,对崩溃案例的结果进行检查。
以下是*_DEATH用法介绍:
1.statement是被测试的代码语句,这里可以使用表达式,也可以直接调用函数结果;
2.regex是一个正则表达式,用来匹配异常时在stderr中输出的内容;
3.编写死亡测试案例时,TEST的第一个参数,即testcase_name,请使用DeathTest后缀。原因是gtest会优先运行死亡测试案例,应该是为线程安全考虑。

以上代码中,通过执行get_throw(NULL)抛出异常来结束程序,在运行过程中gtest执行用例出错以后没有直接抛出异常,而是捕获了此异常信息,以下是运行结果:

其中stderr内容通过正则表达式输出:
在POSIX系统(Linux, Cygwin, 和 Mac)中,gtest的死亡测试中使用的是POSIX风格的正则表达式(关于POSIX风格的正则表达式资料请参照相关文档),宏定义GTEST_USES_POSIX_RE = 1;
在Windows系统中,gtest的死亡测试中使用的是gtest自己实现的简单的正则表达式语法。 相比POSIX风格,gtest的简单正则表达式少了很多内容,比如 (“x|y”), ("(xy)"), ("[xy]") 和(“x{5,7}”)都不支持,宏定义GTEST_USES_SIMPLE_RE=1。

\d 匹配数字
\D 匹配非数字
\f 匹配 \f
\n 匹配 \n
\r 匹配 \r
\s 匹配所有ASCII字符(包括whitespace,\n)
\S 匹配非空格字符
\t 匹配 \t
\v 匹配 \v
\w 匹配所有字母,下划线和数字
\W 匹配所有\w无法匹配的字母
\c matches any literal character c, which must be a punctuation
. matches any single character except \n
A? matches 0 or 1 occurrences of A
A* matches 0 or many occurrences of A
A+ matches 1 or many occurrences of A
^ matches the beginning of a string (not that of each line)
$ matches the end of a string (not that of each line)
xy matches x followed by y

死亡测试有以下两种运行方式:
1.fast方式(默认的方式):
testing::FLAGS_gtest_death_test_style = “fast”;
2.threadsafe方式:
testing::FLAGS_gtest_death_test_style = “threadsafe”;

可以在 main() 里为所有的死亡测试设置测试形式,也可以为某次测试单独设置。gtest会在每次测试之前保存这个标记并在测试完成后恢复,所以可以不需要去管理这部分工作。
以下是所有死亡测试的设置方式:

以下是测试套死亡测试的设置方式:

注意事项:
1.不要在死亡测试里释放内存;
2.不要在父进程里再次释放内存;
3.不要在程序中使用内存堆检查。

运行参数
使用gtest编写的测试案例通常本身就是一个可执行文件,因此运行起来非常方便。同时,gtest也为我们提供了一系列的运行参数(环境变量、命令行参数或代码里指定),使得我们可以对案例的执行进行一些有效的控制。gtest提供了三种设置的途径:
1.系统环境变量
2.命令行参数
3.代码中指定FLAG
其优先级原则是,最后设置的那个会生效。通常情况下的优先级顺序为:命令行参数 > 代码中指定FLAG > 系统环境变量。由于在gtest工程main函数中,gtest通过testing::InitGoogleTest方法直接处理输入参数,因此测试用例可以处理命令行参数。

这样就拥有了接收和响应gtest工程命令行参数的能力。如果需要在代码中指定FLAG,可以使用testing::GTEST_FLAG这个宏来设置。比如相对于命令行参数–gtest_output,可以使用testing::GTEST_FLAG(output) = “xml:”;来设置。注意这里不需要加–gtest前缀了,同时,推荐将这句放置InitGoogleTest之前,这样就可以使得对于同样的参数,命令行参数优先级高于代码中指定。

如果需要gtest的设置系统环境变量,必须注意的是:
1.系统环境变量全大写,比如对于–gtest_output,相应的系统环境变量为GTEST_OUTPUT
2.有一个命令行参数例外,那就是–gtest_list_tests,它是不接受系统环境变量的,只是用来罗列测试案例名称。
以下是所有命令行参数列表:
1.测试案例集合:
命令行参数 说明
–gtest_list_tests 使用这个参数时,将不会执行里面的测试案例,而是输出一个案例的列表。
–gtest_filter 对执行的测试案例进行过滤,支持通配符
? 单个字符

  • 任意字符
  • 排除,如-a 表示除了a
    : 取或,如a:b 表示a或b
    比如下面的例子:
    ./foo_test 没有指定过滤条件,运行所有案例;
    ./foo_test --gtest_filter=* 使用通配符*,表示运行所有案例;
    ./foo_test --gtest_filter=FooTest.* 运行所有“测试案例名称(testcase_name)”为FooTest的案例;
    ./foo_test --gtest_filter=Null:Constructor 运行所有“测试案例名称(testcase_name)”或“测试名称(test_name)”包含Null或Constructor的案例;
    ./foo_test --gtest_filter=-DeathTest. 运行所有非死亡测试案例;
    ./foo_test --gtest_filter=FooTest.*-FooTest.Bar 运行所有“测试案例名称(testcase_name)”为FooTest的案例,但是除了FooTest.Bar这个案例
    –gtest_also_run_disabled_tests 执行案例时,同时也执行被置为无效的测试案例。关于设置测试案例无效的方法为:在测试案例名称或测试名称中添加DISABLED前缀。
    –gtest_repeat=[COUNT] 设置案例重复运行次数:
    –gtest_repeat=1000:重复执行1000次,即使中途出现错误;
    –gtest_repeat=-1: 无限次数执行
    –gtest_repeat=1000 --gtest_break_on_failure:重复执行1000次,并且在第一个错误发生时立即停止,这个功能对调试非常有用。
    –gtest_repeat=1000 --gtest_filter=FooBar:重复执行1000次测试案例名称为FooBar的案例。

2.测试案例输出
命令行参数 说明
–gtest_color=(yes|no|auto) 输出命令行时是否使用一些五颜六色的颜色,默认是auto。
–gtest_print_time 输出命令行时是否打印每个测试案例的执行时间,默认是不打印的。
–gtest_output=
xml[:DIRECTORY_PATH|:FILE_PATH] 将测试结果输出到一个xml中。
–gtest_output=xml: 不指定输出路径时,默认为案例当前路径。
–gtest_output=xml:d:\ 指定输出到某个目录
–gtest_output=xml:d:\foo.xml 指定输出到d:\foo.xml
如果不是指定了特定的文件路径,gtest每次输出的报告不会覆盖,而会以数字后缀的方式创建。

3.对案例的异常处理
命令行参数 说明
–gtest_break_on_failure 调试模式下,当案例失败时停止,方便调试。
–gtest_throw_on_failure 当案例失败时以C++异常的方式抛出。
–gtest_catch_exceptions 是否捕捉异常。gtest默认是不捕捉异常的,因此假如测试案例抛了一个异常,很可能会弹出一个对话框,这非常的不友好,同时也阻碍了测试案例的运行。如果想不弹这个框,可以通过设置这个参数来实现。如将–gtest_catch_exceptions设置为一个非零的数。
注意:该参数只在Windows下有效。

以下是命令行参数列表在使用过程中遇到的一些问题总结:
1.同时使用–gtest_filter和–gtest_output=xml:时,在xml测试报告中能否只包含过滤后的测试案例的信息。
2.有时在代码中设置 testing::GTEST_FLAG(catch_exceptions) = 1和在命令行中使用–gtest_catch_exceptions结果稍有不同,在代码中设置FLAG方式有时候捕捉不了某些异常,但是通过命令行参数的方式一般都不会有问题。最后处理办法是既在代码中设置FLAG,又在命令行参数中传入–gtest_catch_exceptions,估计是gtest在catch_exceptions方面不够稳定的原因导致。

附件
以下是一些素材来源和更详细的gtest介绍资料,本文档暂时只提供一些基础的介绍和演示,如果还需要更详细的了解,建议阅读gtest源代码或者上网查找更深入详细的资料:

你可能感兴趣的:(QT,C++知识点汇总)