C++基于 googletest 做单元测试

马谦马谦马谦 程序员1 5,149字数 3505阅读 11 分 41 秒阅读模式

一、安装 googletest

单测对程序员而言是提升代码质量最重要、最有效的一个措施,对程序员来说,要想写一个好的程序,那么必定少不了好的单元测试。 googletest(gtest) 是 google 开发出来的一个开源的、跨平台的测试框架,是 C++中最出名的测试框架。

gtest 支持 linux 、 windows 以及 mac 系统,安装它依赖下面几项:

  1. gtest 源码:gtest 属于开源项目,代码仓库 https://github.com/google/googletest
  2. cmake:gtest 使用 cmake 构建项目,编译需要 cmake 环境,cmake 下载地址
  3. 编译器:linux 环境下可使用 g++编译,windows 环境下使用 vs 或者 clion 等工具编译,mac 环境使用 xcode 或 clion 等工具编译。

这里的测试环境为 mac+clion(付费),clion 下载地址,选择 clion 是因为 clion 跨平台。

windows 环境除了配置 clion 编译环境以外,其他步骤和 mac 系统一致。

1.1 环境配置

第一步,先使用 git 克隆代码到本地,注意最好不要放到中文路径了。

第二步,安装 cmake,不同系统的的安装方式不一样,windows 在上面的页面下载一直下一步安装就行了,其他系统可以直接使用对应平台的包管理工具安装。

第三步,安装 clion,linux 和 mac 环境下安装 clion 和 gcc 环境就可以使用了,windows 配置 clion 编译环境可参考 Window10 上 CLion 极简配置教程

1.2 编译 gtest 库

配置好环境后,使用 clion 打开代码目录,然后载入代码目录,选择 gtest 项目编译生成:

C++基于googletest做单元测试-图片1

编译成功后生成 libgtestd.a 文件到 cmake 编译路径的 lib 路径下:

C++基于googletest做单元测试-图片2

生成的 libgtestd.a 即为 gtest 的库文件,项目中引用这个库文件就能使用 gtest 了。

二、使用 googletest

2.1 引入库

将 libgtestd.a 文件拷贝到代码根路径的 lib 路径下,在 CMakeList.txt 中加上以下内容:

2.2 引入头文件

拷贝 googletest/include 目录下的 gtest 目录到当前目录下,然后在 CMakeList.txt 中添加上对应的调用:

然后在代码中添加头文件 gtest/gtest.h 就可以使用了。

2.3 测试

添加代码 add.cpp

执行结果:

![](/Users/maqian/Library/Application Support/typora-user-images/image-20200208101215701.png)

三、 gtest 的使用教程

参考文档:Googletest Primer,google 官方出品。

3.1 基本用法

gtest 最基本的用法就是断言,它内部提供了很多种断言方式,例如:

其中 ASSERT_*的断言,在条件不满足后会终止,而 EXPECT_*不会终止。

以上面的代码为例,代码编写了一个 add 函数,返回两个传参的和:

然后引入 gtest 并写了三个测试用例:

三个用例分别表示:

  • 测试零值相加
  • 测试正数相加
  • 测试负数相加

主函数中添加启动 gtest 的入口:

运行程序,系统就会自动调用三个测试用例的函数来测试,并输出测试报告:

如果中间有断言失败的地方,报告也会表达出来。例如修改上面测试中的负数相加函数来制造错误场景:

再执行测试,报告中就会把不通过的案例展示出来,并且会定位到对应的行,打印出失败的详细原因:

C++基于googletest做单元测试-图片3

当案例执行失败后,我们也可以打印出一些我们自己的数据以供调试使用,例如:

案例失败后,不仅会打印出失败原因,还会打印出我们自己添加的语句:

C++基于googletest做单元测试-图片4

3.2 常用断言

基本断言

致命断言 非致命断言 验证
ASSERT_TRUE(condition); EXPECT_TRUE(condition); 条件 condition 为真
ASSERT_FALSE(condition); EXPECT_FALSE(condition); 条件 condition 为假

二进制比较

致命断言 非致命断言 验证
ASSERT_EQ(val1, val2); EXPECT_EQ(val1, val2); val1 == val2
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); val1 >= val2

字符串比较

致命断言 非致命断言 验证
ASSERT_STREQ(str1,str2); EXPECT_STREQ(str1,str2); 两个 c 字符串内容相同
ASSERT_STRNE(str1,str2); EXPECT_STRNE(str1,str2); 两个 c 字符串内容不同
ASSERT_STRCASEEQ(str1,str2); EXPECT_STRCASEEQ(str1,str2); 两个 c 字符串内容相同 (忽略大小写)
ASSERT_STRCASENE(str1,str2); EXPECT_STRCASENE(str1,str2); 两个 c 字符串内容不同 (忽略大小写)

四、使用 Test Fixtures

Test Fixtures 使用场景:测试案例需要初始化数据或者多个测试案例使用相同的测试数据。

例如在对一个的做单元测试时,测试 pop 功能,按照上面的测试方法,测试案例得这么写:

测试过程可以描述为:

  1. 创建一个栈对象的实例 s 。
  2. 推入 3 个元素,以便后面 pop 使用。
  3. 开始测试 pop 。

从直观上来看,所有和 pop 相关的测试案例都要这么来写,要先推入元素,再弹出。而实际上,步骤 1 和步骤 2 是和本轮测试无关,它只起到了初始化数据的作用,它是多余的,但是所有的测试案例又不得不做这一步操作。那么有没有办法解决这个问题呢?有!Test Fixtures 的就是解决这种问题的,它可以在测试案例开始前自动生成好需要的数据。

定义了一个简单的的类:

再定义一个测试类 stack_test

它要公有继承于::testing::Test,其中的 SetUpTearDown 函数分别是初始化和清理函数,也就是类生成前和使用后要做的工作。

此时使用 TEST_F 宏定义来测试:

在执行 TEST_F 之前,gtest 会自动构建一个 stack_test 的实例,并执行 SetUp 函数。也就是说,当真正执行到我们的测试代码的时候,就已经存在一个初始化好的测试环境了。这个时候可以直接访问 stack_test 的内部成员,通过成员变量来做单元测试。

原理来看其实很简单,就是把初始化的过程交给了 gtest 来完成,它来帮我们实例对象,进行初始化,我们直接用就行。

测试结果:

C++基于googletest做单元测试-图片5

五、其他

5.1 clion 环境跨平台使用 gtest

如何在不改变 CMakeList.txt 的情况下跨平台使用 gtest?配置 CMakeList,根据不同平台读取不同的库:

六、参考

Google Test support

Googletest Primer

  最后更新:2020-2-8
马谦马谦马谦
  • 本文由 马谦马谦马谦 发表于 2020 年 1 月 30 日 22:23:49
  • 转载请务必保留本文链接:https://www.dyxmq.cn/program/usage-of-googletest.html
解决gvm工具无法下载安装包的问题 Golang

解决 gvm 工具无法下载安装包的问题

GVM(Go Version Manager) 是一款用于管理和切换不同 Go 语言版本的工具。它允许用户在同一台计算机上轻松安装、使用和管理多个 Go 版本,同时还能确保项目之间的依赖关系井然有序。 GVM 的主...
goland配置proto文件搜索路径 Golang

goland 配置 proto 文件搜索路径

默认情况下,goland(jetbrains 家的软件:idea 、 pycharm 、 phpstorm 以及 webstorm 都是一样) 安装 protobuf 插件后只会在一个特定的库路径下搜索 proto 文件。如果...
C++文件输入输出流fstream的基本用法 C/C++

C++文件输入输出流 fstream 的基本用法

一、文件流 C++的 IO 类中定义了三个文件读写流 fstream 、 ifstream 以及 ofstream,它们都继承于相同的父类 istream,通过不同的实现以实现不同的文件流操作。 三者的区别为: if...
    • 目下不食荤
      目下不食荤 0

      太清晰了,谢谢博主!!

    匿名

    发表评论

    匿名网友
    :?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:
    确定

    拖动滑块以完成验证