谷歌单元测试GTest测试方法使用
什么是googletest?
googletest简介
GoogleTest 是 Google 的 C++ 测试和模拟框架,可以帮助程序员测试C++程序的结果预期,GoogleTest 的代码用cmake管理,可以使用cmake进行编译程dll在程序中使用。 googletest一般也可以简称为gtest, 最新版本GoogleTest 需要符合 C++11 标准或更新标准的代码库和编译器。
gtest官网:https://google.github.io/googletest/
git仓库:https://github.com/google/googletest
谁在使用 GoogleTest?
除了 Google 的许多内部项目,GoogleTest 还被以下著名项目使用:
- 该铬项目(Chrome浏览器和Chrome OS背后)。
- 该LLVM编译器。
- Protocol Buffers,Google 的数据交换格式。
- 在OpenCV的计算机视觉库。 相关开源项目
相关开源项目
GTest Runner是一个基于 Qt5 的自动化测试运行器和图形用户界面,具有适用于 Windows 和 Linux 平台的强大功能。
GoogleTest UI是一个测试运行器,它运行您的测试二进制文件,允许您通过进度条跟踪其进度,并显示测试失败列表。 单击一个显示失败文本。 GoogleTest UI 是用 C# 编写的。
GTest TAP Listener是GoogleTest 的一个事件监听器,它实现了测试结果输出的 TAP 协议。 如果您的测试运行器了解 TAP,您可能会发现它很有用。
gtest-parallel是一个测试运行器,它并行运行来自二进制文件的测试以提供显着的加速。
GoogleTest Adapter 是一个 VS Code 扩展,允许在树视图中查看 GoogleTest,并运行/调试您的测试。
C++ TestMate是一个 VS Code 扩展,允许在树视图中查看 GoogleTest,并运行/调试您的测试。
Cornichon是一个小型 Gherkin DSL 解析器,可为 GoogleTest 生成存根代码。
googletest的下载与编译
github链接:https://github.com/google/googletest
cmake gui编译
添加代码路径和生成后的目录,点击configure, 我编译的是win32版本

增加一个编译选项 CMAKE_DEBUG_POSTFIX,类型为STRING, value为 _d(这个随便写,只是为了区分debug和release的
库)这样编译后的debug库就会自带一个d后缀,与release的好区分。

点击generate

出现以上结果表示sln生成ok, 点击open project即可打开项目,一共66个子项目,其中有lib和googletest的测试代码。

右键INSTALL项目,生成解决方案,即可把.h .lib .dll 提取到cmake编译时设置的CMAKE_INSTALL_PREFIX目录里,我的生成结果:

bin目录里是dll

很多项目都可以用cmake进行编译,只要在源码根目录看到了CMakeLists.txt, 那么这种项目就可以使用cmake进行编译,比如:
- 圣。
- 开放CV
- jsoncpp
- log4cplus
- 谷歌测试
- 谷歌铬
- webrtc
- glog
- 还有很多 。。。
在vs2019中使用googletest
一般编译后,都是.h .lib .dll, 在vs中进行配置就可以使用,下面是gtest的测试代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <iostream>#include "gtest/gtest.h"#pragma comment(lib, "gtestd.lib")int add(int a, int b) { return a + b;}//编写测试caseTEST(testCase, test0) { EXPECT_EQ(add(2, 3), 5); //判断结果是不是等于5,EXPECT_EQ表示 "等于"}int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();} |
运行结果

GTest的一些基本概念
要测试一个类或函数,我们需要对其行为做出断言。 当一个断言失败时,Google Test会在屏幕上输出该代码所在的源文件及其所在的位置行号,以及错误信息。 也可以在编写断言时,提供一个自定义的错误信息,这个信息在失败时会被附加在Google Test的错误信息之后。
断言常常成对出现,它们都测试同一个类或者函数,但对当前功能有着不同的效果。 ASSERT_*版本的断言失败时会产生致命失败,并结束当前函数。 EXPECT_*版本的断言产生非致命失败,而不会中止当前函数。 通常更推荐使用EXPECT_*断言,因为它们运行一个测试中可以有不止一个的错误被报告出来。 但如果在编写断言如果失败,就没有必要继续往下执行的测试时,你应该使用ASSERT_*断言。 因为失败的ASSERT_*断言会立刻从当前的函数返回,可能会跳过其后的一些的清洁代码,这样也许会导致空间泄漏。
GTest的断言
1、布尔值检查
| 致命断言 | 非致命断言 | 验证 |
|---|---|---|
| ASSERT_TRUE(条件)); | EXPECT_TRUE(条件)); | 条件为真 |
| ASSERT_FALSE(条件)); | EXPECT_FALSE(条件)); | 条件为假 |
2、数值型数据检查
| 致命断言 | 非致命断言 | 验证 |
|---|---|---|
| ASSERT_EQ(预期,实际)); | EXPECT_EQ(预期,实际)); | 预期 == 实际 |
| ASSERT_NE(val1, val2); | EXPECT_NE(val1, val2); | val1 != val2 |
| ASSERT_LT(val1, val2); | EXPECT_LT(val1, val2); | h1 < 小时2 |
| ASSERT_LE(val1, val2); | EXPECT_LE(val1, val2); | h1 < = h2 |
| ASSERT_GT(val1, val2); | EXPECT_GT(val1, val2); | val1 > val2 |
| ASSERT_GE(val1, val2); | EXPECT_GE(val1, val2); | val1 >= val2 |
3、字符串比较
| 致命断言 | 非致命断言 | 验证 |
|---|---|---|
| ASSERT_STREQ(expected_str, actual_str); | EXPECT_STREQ(expected_str, actual_str); | 两个C字符串有相同的内容 |
| ASSERT_STRNE(str1, str2); | EXPECT_STRNE(str1, str2); | 两个C字符串有不同的内容 |
| ASSERT_STRCASEEQ(expected_str, actual_str); | EXPECT_STRCASEEQ(expected_str, actual_str); | 两个C字符串有相同的内容,忽略大小写 |
| ASSERT_STRCASENE(str1, str2); | EXPECT_STRCASENE(str1, str2); | 两个C字符串有不同的内容,忽略大小写 |
4、异常检查
| 致命断言 | 非致命断言 | 验证 |
|---|---|---|
| ASSERT_THROW(语句,exception_type); | EXPECT_THROW(语句, exception_type); | 语句引发给定类型的异常 |
| ASSERT_ANY_THROW(语句); | EXPECT_ANY_THROW(语句); | 语句引发任何类型的异常 |
| ASSERT_NO_THROW(语句); | EXPECT_NO_THROW(语句); | 语句不引发任何异常 |
5、浮点型检查
| 致命断言 | 非致命断言 | 验证 |
|---|---|---|
| ASSERT_FLOAT_EQ(预期,实际)); | EXPECT_FLOAT_EQ(预期,实际)); | 两个浮点值几乎相等 |
| ASSERT_DOUBLE_EQ(预期,实际)); | EXPECT_DOUBLE_EQ(预期,实际)); | 两个双精度值几乎相等 |
对相近的两个数比较:
| 致命断言 | 非致命断言 | 验证 |
|---|---|---|
| ASSERT_NEAR(val1, val2, abs_error); | EXPECT_NEAR*(val1, val2, abs_error*); | val1 和 val2 之间的差异不超过给定的绝对误差 |
6、此外还有类型检查、谓词检查等
事件机制
全局事件
要实现全局事件,必须写一个类,继承testing::Environment类,实现里面的SetUp和TearDown方法。
- SetUp()方法在所有案例执行前执行
- TearDown()方法在所有案例执行后执行
还需要告诉gtest添加这个全局事件,我们需要在main函数中通过testing::AddGlobalTestEnvironment方法将事件挂进来,也就是说,我们可以写很多个这样的类,然后将他们的事件都挂上去。
TestSuite事件
我们需要写一个类,继承testing::Test,然后实现两个静态方法
- SetUpTestCase() 方法在第一个TestCase之前执行
- TearDownTestCase() 方法在最后一个TestCase之后执行
在编写测试案例时,我们需要使用TEST_F这个宏,第一个参数必须是我们上面类的名字,代表一个TestSuite。
TestCase事件
TestCase事件是挂在每个案例执行前后的,实现方式和上面的几乎一样,不过需要实现的是SetUp方法和TearDown方法:
- SetUp()方法在每个TestCase之前执行
- TearDown()方法在每个TestCase之后执行
以下案例解决说明上述三个事件的使用
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
#include <iostream>#include <map>#include "gtest/gtest.h"#pragma comment(lib, "gtestd.lib")using namespace std;class Student {public: Student() { age = 0; } Student(int a) { age = a; } void print() { cout << "*********** " << age << " **********" << endl;; }private: int age;};class FooEnvironment : public testing::Environment {public: virtual void SetUp() { std::cout << "Foo FooEnvironment SetUP" << std::endl; } virtual void TearDown() { std::cout << "Foo FooEnvironment TearDown" << std::endl; }};static Student* s;//在第一个test之前,最后一个test之后调用SetUpTestCase()和TearDownTestCase()class TestMap :public testing::Test{public: static void SetUpTestCase() { cout << "SetUpTestCase()" << endl; s = new Student(23); } static void TearDownTestCase() { delete s; cout << "TearDownTestCase()" << endl; } void SetUp() { cout << "SetUp() is running" << endl; } void TearDown() { cout << "TearDown()" << endl; }};TEST_F(TestMap, Test1){ // you can refer to s here s->print();}int main(int argc, char** argv){ testing::AddGlobalTestEnvironment(new FooEnvironment); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();} |
运行结果

参数化
当考虑多次要为被测函数传入不同的值的情况时,可以按下面的方式去测试。 必须添加一个类,继承testing::TestWithParam。 其中T就是你需要参数化的参数类型,如下面的案例是int型参数。 (官方文档上的案例)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#include<gtest/gtest.h>// Returns true iff n is a prime number.bool IsPrime(int n){ // Trivial case 1: small numbers if (n <= 1) return false; // Trivial case 2: even numbers if (n % 2 == 0) return n == 2; // Now, we have that n is odd and n >= 3. // Try to divide n by every odd number i, starting from 3 for (int i = 3; ; i += 2) { // We only have to try i up to the squre root of n if (i > n/i) break; // Now, we have i <= n/i < n. // If n is divisible by i, n is not prime. if (n % i == 0) return false; } // n has no integer factor in the range (1, n), and thus is prime. return true;}class IsPrimeParamTest : public::testing::TestWithParam<int>{};TEST_P(IsPrimeParamTest, HandleTrueReturn){ int n = GetParam(); EXPECT_TRUE(IsPrime(n));}//被测函数须传入多个相关的值INSTANTIATE_TEST_CASE_P(TrueReturn, IsPrimeParamTest, testing::Values(3, 5, 11, 23, 17));int main(int argc, char **argv){ testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();} |

浙公网安备 33010602011771号