gtest 是一个google开源的C++单元测试的框架,它是跨平台的,可应用在windows、linux、Mac等OS平台。https://github.com/google/googletest
编译gtest
下载gtest源码
[heql@ubuntu gtest]$ git clone https://github.com/google/googletest.git
编译
[heql@ubuntu gtest]$ cd googletest/googletest
[heql@ubuntu googletest]$ g++ -isystem $(pwd)/include -I$(pwd) -lpthread -c $(pwd)/src/gtest-all.cc
编译成功后,会在当前目录下生成一个gtest-all.o
文件。
生成静态库
[heql@ubuntu googletest]$ ar -rv libgtest.a gtest-all.o
执行成功后,会在当前目录下生成一个libgtest.a
的静态库,以后编译测试程序链接的时候,指定这个静态库和包含本目录下的include
头文件。或者可以将这个静态库和头文件拷贝到环境变量中,如下,将libgtest.a
拷贝到/usr/local/lib/
,将include
下的gtest
目录拷贝到/usr/local/include/
。
[heql@ubuntu googletest]$ cp libgtest.a /usr/local/lib/
[heql@ubuntu googletest]$ sudo cp include/gtest/ /usr/local/include/ -rf
简单测试
Factorial函数
在factorial.cc
文件中,创建一个求阶乘的函数Factorial
,代码如下:
1 | int Factorial(int n) { |
Factorial函数的测试用例
在factorial_unittest.cc
文件中,创建测试Factorial
函数的测试用例,代码如下:
1 |
|
- TEST() 宏用来定义和命名测试函数,这些是不返回值的普通C++函数。
- 在此函数中,可以使用任何有效的C++语句,使用各种Google Test断言来检查值。
- 测试的结果由断言确定,如果测试中的任何断言失败(致命或非致命),或者如果测试崩溃,则整个测试失败。 否则,它成功。
TEST() 的第一个参数是测试用例的名称,第二个参数是测试用例中的测试名称。 这两个名称必须是有效的C ++标识符,并且它们不应包含下划线(_)。一个测试的全称包括了包含它的测试用例名称,及其独立的测试名称。不同测试用例中的独立测试可以有相同的测试名称。
gtest 通过测试用例对测试结果进行分组,因此逻辑相关的测试应该在同一测试用例中; 换句话说,它们的 TEST() 的第一个参数应该是相同的。 在上面的例子中,有两个测试,Zero
和Positive
,属于同一个测试用例FactorialTest
。
EXPECT_EQ
宏用来比较两个数字是否相等。
::testing::InitGoogleTest(&argc, argv)
,将命令行参数传递给gtest,进行一些初始化操作。如:--gtest_list_tests
会列出所有的测试用例和测试名称。
[heql@ubuntu gtest]$ ./factorial_unittest --gtest_list_tests
FactorialTest.
Zero
Positive
可以执行./factorial_unittest --help
查看命令行支持的参数。
RUN_ALL_TESTS()
表示运行所有测试案例。
编译程序
[heql@ubuntu gtest]$ g++ factorial_unittest.cc factorial.cc -o factorial_unittest -lgtest -lpthread
运行上面的单元测试程序,输出如下:
FactorialTest
测试用例,运行了两次测试,分别是Zero
和Positive
,测试的结果是正确的。注意: Zero
和Positive
的测试,执行的顺序是不确定的。
断言
gtest 断言是类似于函数调用的宏。您可以通过对其行为进行断言来测试类或函数。当断言失败时,gtest 会打印断言的源文件和行号位置以及失败消息。也可以提供自定义失败消息,该消息将附加到测试的信息中。
断言失败
把上面的FactorialTest
测试用例的Positive
测试改为:
1 | TEST(FactorialTest, Positive) { |
编译、运行程序,输出如下:
FactorialTest
测试用例,运行了两次测试,分别是Zero
和Positive
,Zero
测试成功,Positive
测试失败:在factorial_unittest.cc文件的15行,Factorial(3)
执行的结果应该是6,但是EXPECT_EQ
的断言是7。
自定义断言失败消息
要提供自定义失败消息,只需使用<<运算符或一系列此类运算符将其流式传输到宏中即可。把上面的FactorialTest
测试用例的Positive
测试改为:
1 | TEST(FactorialTest, Positive) { |
编译、运行程序,输出如下:
可以看到输出的失败消息中,有自定义的消息Factorial(3) unequal to 7.
。
布尔值检查
这些断言做基本的真/假条件测试。
gtest 中,断言的宏可以理解为分为两类,一类是ASSERT系列,一类是EXPECT系列。
- ASSERT_* 系列的断言,当检查点失败时,退出当前函数(注意:并非退出当前用例)。
- EXPECT_* 系列的断言,当检查点失败时,继续往下执行。
把上面的FactorialTest
测试用例的Positive
测试改为:
1 | TEST(FactorialTest, Positive) { |
编译、运行程序,输出如下:
FactorialTest
测试用例,运行了两次测试,分别是Zero
和Positive
,Zero
测试成功,Positive
的Factorial(3)
和Factorial(8)
测试失败。
把上面的FactorialTest
测试用例的Positive
测试改为:
1 | TEST(FactorialTest, Positive) { |
编译、运行程序,输出如下:
FactorialTest
测试用例,运行了两次测试,分别是Zero
和Positive
,Zero
照常测试,测试成功,Positive
的Factorial(3)
测试失败后,Factorial(8)
不在进行测试。
数值型数据检查
比较两个值的断言。
字符串检查
该组中的断言比较两个C字符串的值。 如果要比较两个字符串对象,请改用EXPECT_EQ,EXPECT_NE等。
ASSERT_EQ 在两个C字符串上使用,它会测试它们是否在同一个内存位置,而不是它们具有相同的值。因此,如果想比较C字符串(例如const char *)的值,请使用 ASSERT_STREQ 。要比较两个字符串对象,应该使用 ASSERT_EQ 。
要断言C字符串为NULL,请使用 ASSERT_STREQ(NULL,c_string)
,NULL指针和空字符串被认为是不同的。
如下代码,测试成功:
1 |
|
浮点数检查
Test Fixtures(测试装饰类) 对多个测试使用相同的数据配置
如果你发现自己写了两个或更多的测试来操作类似的数据,你可以使用测试装饰类。它允许您为几个不同的测试重复使用相同的对象配置。
要创建测试装饰类,只需:
- 从
::testing::Test
派生一个类。 使用protected
:或public
:开始它的主体,因为我们想从子类访问fixture
成员。 - 在类中,声明你打算使用的任何对象。
- 如果需要,可以编写默认构造函数或
SetUp
函数来为每个测试准备对象。 - 如果需要,写一个析构函数或
TearDown
函数来释放你在SetUp
中分配的任何资源。
当使用夹具时,使用 TEST_F() 而不是 TEST()
Foo类
在foo.h
文件中,创建一个类Foo
,代码如下:
1 |
|
Foo类的测试用例
在foo_unittest.cc
文件中,创建测试Foo
类的测试用例,使用TEST() 宏进行测试,代码如下:
1 |
|
在FooTest
测试用例的测试DefaultConstructor
和SetValue
分别创建一个Foo
的实例。可以使用TEST_F()
来共享这个实例,指向创建一个实例即可,代码如下:
1 |
|
在这里SetUp
和TearDown
函数并没有使用,如果需要可以使用SetUp
函数对foo
实例进行一些初始化,TearDown
函数来释放foo
的资源。
参数化测试
在设计测试用例时,经常需要考虑给被测函数传入不同的值的情况。我们之前的做法通常是写一个通用方法,然后编写在测试案例调用它。即使使用了通用方法,这样的工作也是有很多重复性的。
IsPrime函数
IsPrime
函数是用来判断一个整数是否为素数,代码如下:
1 | bool IsPrime(int n) { |
IsPrime函数的测试用例
使用TEST()宏进行测试
1 |
|
使用值参数化进行测试
- 添加一个类,继承
testing::TestWithParam<T>
,其中T就是你需要参数化的参数类型,比如上面的例子,需要参数化一个int型的参数。
1 | class IsPrimeTest : public ::testing::TestWithParam<int> { |
- 使用一个新的宏TEST_P,在 TEST_P 宏里,使用GetParam()获取当前的参数的具体值。
1 | TEST_P(IsPrimeTest, TestReturnTrue) { |
- 使用 INSTANTIATE_TEST_CASE_P 这宏来告诉gtest要测试的参数范围。
1 | INSTANTIATE_TEST_CASE_P(PrimeParamTest, |
第一个参数是测试用例的前缀,可以任意取。
第二个参数是测试案例的名称,需要和之前定义的参数化的类的名称相同,如:IsPrimeTest
。
第三个参数是可以理解为参数生成器,上面的例子使用test::Values
表示使用括号内的参数。Google提供了一系列的参数生成的函数:
编译、运行程序,输出如下:
可以看到参数-1,0的返回值不是true
,而是false
。其余的参数测试是正确的。
其他
gtest 还提供了一些高级的用法,可以参考:
https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md