什么是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 ListenerGoogleTest 的一个事件监听器,它实现了测试结果输出的 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版本

gtest 项目 gtest github_#include

 

 

 

增加一个编译选项 CMAKE_DEBUG_POSTFIX,类型为STRING, value为 _d(这个随便写,只是为了区分debug和release的

库)这样编译后的debug库就会自带一个d后缀,与release的好区分。

gtest 项目 gtest github_gtest 项目_02

 

 

点击generate

gtest 项目 gtest github_EQ_03

 

 

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

gtest 项目 gtest github_Google_04

 

 

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

gtest 项目 gtest github_EQ_05

 

 

bin目录里是dll

gtest 项目 gtest github_#include_06

 

 

很多项目都可以用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;

}

 

//编写测试case

TEST(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 项目 gtest github_gtest 项目_07

 

 

 


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方法。

  1. SetUp()方法在所有案例执行前执行
  2. TearDown()方法在所有案例执行后执行

还需要告诉gtest添加这个全局事件,我们需要在main函数中通过testing::AddGlobalTestEnvironment方法将事件挂进来,也就是说,我们可以写很多个这样的类,然后将他们的事件都挂上去。

TestSuite事件

我们需要写一个类,继承testing::Test,然后实现两个静态方法

  1. SetUpTestCase() 方法在第一个TestCase之前执行
  2. TearDownTestCase() 方法在最后一个TestCase之后执行

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

TestCase事件

TestCase事件是挂在每个案例执行前后的,实现方式和上面的几乎一样,不过需要实现的是SetUp方法和TearDown方法:

  1. SetUp()方法在每个TestCase之前执行
  2. 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();
}


运行结果

gtest 项目 gtest github_Google_08

 

 

 


参数化

当考虑多次要为被测函数传入不同的值的情况时,可以按下面的方式去测试。 必须添加一个类,继承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();
}