【gtest】 C++ 的测试框架之使用 gtest 编写单元测试

目录

🌊前言

🌊使用 cmake 启动并运行 gtest

🌍1. 设置项目

🌍2. 创建并运行二进制文件

🌊1. gtest 入门

🌍1.1 断言(assertions)

🌍1.2 简单测试

🌍1.3 测试夹具:为多个测试使用相同的数据配置


🌊前言

gtest 是 Google 开发的一个用于 C++ 的测试框架,广泛应用于编写和运行单元测试,并且支持任何类型的测试,而不仅仅是单元测试。

本教程分成以下部分:

  • GoogleTest 入门(GoogleTest Primer - 教你如何使用 GoogleTest 编写简单的测试。如果你是 GoogleTest 新手,请先阅读此部分。
  • GoogleTest 高级教程(GoogleTest Advanced - 当你完成入门教程并想充分利用 GoogleTest 时,请阅读此部分。
  • GoogleTest 示例(GoogleTest Samples - 介绍一些 GoogleTest 示例。
  • GoogleTest 常见问题解答(GoogleTest FAQ- 有问题吗?想要一些提示?请先查看这里。
  • 模拟初学者指南 (Mocking for Dummies- 教你如何创建模拟对象并在测试中使用它们。
  • 模拟秘籍 (Mocking Cookbook - 包含常见模拟用例的技巧和方法。
  • 模拟速查表 (Mocking Cheat Sheet - 用于匹配器、动作、不变量等的便捷参考。
  • 模拟常见问题解答(Mocking FAQ - 包含一些特定于模拟的常见问题的答案。

注意:

  • 本教程使用 cmake 启动并运行 GoogleTest:需提前安装 CMake。
  • 术语:测试(Test)、测试用例(Test Case)和测试套件(Test Suite)。

🌊使用 cmake 启动并运行 gtest

🌍1. 设置项目

CMake 使用 CMakeLists.txt 来配置项目的构建系统【使用该文件设置项目,并声明对 gtest 的依赖】

首先,创建一个项目的目录:

mkdir my_project && cd my_project

接下来,将创建 CMakeLists.txt 文件并声明对 GoogleTest 的依赖。

在项目目录(my_project)中,创建一个名为 CMakeLists.txt 的文件:

vim CMakeLists.txt

其内容如下:

cmake_minimum_required(VERSION 3.14)
project(my_project)# 设置 C++ 标准为 C++14
set(CMAKE_CXX_STANDARD 14)
# 强制要求编译器支持所选的 C++ 标准
set(CMAKE_CXX_STANDARD_REQUIRED ON)# 包含 FetchContent 模块,用于从外部资源获取依赖项
include(FetchContent)
FetchContent_Declare(googletestURL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
# 对于 Windows 系统:防止覆盖父项目的编译器/链接器设置
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# 使得 GoogleTest 可用
FetchContent_MakeAvailable(googletest)

这个文件中包括了以下部分:

  1. cmake_minimum_required(VERSION 3.14):指定了 CMake 的最低版本要求。
  2. project(my_project):定义了项目的名称。
  3. set(CMAKE_CXX_STANDARD 14)set(CMAKE_CXX_STANDARD_REQUIRED ON):设置了 C++ 标准为 C++14,且要求编译器支持此标准。
  4. include(FetchContent):包含了 CMake 的 FetchContent 模块,用于从外部资源(如 GitHub)获取依赖项。
  5. FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip):声明了对 GoogleTest 的依赖,指定了下载地址。
  6. set(gtest_force_shared_crt ON CACHE BOOL "" FORCE):对于 Windows 系统,防止覆盖父项目的编译器/链接器设置。
  7. FetchContent_MakeAvailable(googletest):获取并使 GoogleTest 可用。

🌍2. 创建并运行二进制文件

将 gtest 声明为一个依赖项后,你就可以在自己的项目中使用 GoogleTest 代码。

举例来说,在 my_project 目录中创建一个名为 hello_test.cc 的文件:

vim hello_test.cc 

内容如下:

#include <gtest/gtest.h>// 展示一些基本断言。
TEST(HelloTest, BasicAssertions) {// 期望两个字符串不相等。EXPECT_STRNE("hello", "world");// 期望相等。EXPECT_EQ(7 * 6, 42);
}

要构建代码,需要将以下内容添加到你的 CMakeLists.txt 文件末尾:

# 启用测试
enable_testing()# 声明要测试的可执行文件
add_executable(hello_testhello_test.cc
)
# 链接 GoogleTest 主要库
target_link_libraries(hello_testGTest::gtest_main
)# 包含 GoogleTest 模块
include(GoogleTest)
# 使用 gtest_discover_tests 函数来自动发现并添加测试
gtest_discover_tests(hello_test)

上述配置启用了 CMake 中的测试,声明了要构建的 C++ 测试二进制文件(hello_test),并将其链接到 GoogleTest(gtest_main)。

最后两行启用了 CMake 的测试运行器,使用 GoogleTest 的 CMake 模块来发现包含在二进制文件中的测试。

现在你可以依据下面指令构建和运行你的测试:

1.

cmake -S . -B build
  • 告诉 CMake 在当前目录(-S .)中查找 CMakeLists.txt 文件,并在指定的构建目录 build 中生成构建系统文件(-B build)。

2. 

cmake --build build
  • cmake 是调用 CMake 工具的命令。
  • --build 是用于告诉 CMake 执行构建操作的选项。
  • build 是构建目录的路径,指定了 CMake 在build 路径下执行构建操作。

3.

cd build && ctest
  • cd build 进入构建目录。
  • ctest 会查找构建目录中的测试,并执行它们。

显示如下内容:

恭喜!你成功地构建并运行了一个使用 GoogleTest 的测试二进制文件。


🌊1. gtest 入门

使用 gtest 时,首先要会编写断言(assertions),这些是检查条件是否为真的语句。

一个断言的结果可以是成功、非致命失败致命失败【如果发生致命失败,它会中止当前函数;否则程序会正常继续执行】

测试使用断言来验证被测试代码的行为。如果一个测试崩溃或有一个失败的断言,那么它失败;否则它成功。

  • 一个测试套件(test suite)包含一个或多个测试(test)。应该将你的测试(test)分组到反映被测代码结构的测试套件(test suite)中。
  • 一个测试程序可以包含多个测试套件(test suite)。

接下来,我们将解释如何编写一个测试程序,从单个断言级别开始,逐步构建到测试和测试套件。


🌍1.1 断言(assertions)

断言(assertions)是类似函数调用的宏。你可以通过对其行为进行断言来测试一个类或函数。当一个断言失败时,gtest 会打印断言的源文件和行号位置,以及一个失败消息。你还可以提供一个自定义的失败消息,它将附加到 gtest 的消息中。

这些断言成对出现,测试相同的事物,但对当前函数有不同的影响。

  • ASSERT_* 版本在失败时会生成致命失败,并中止当前函数。
  • EXPECT_* 版本生成非致命失败,不会中止当前函数。

通常情况下,优先使用 EXPECT_*,因为它们允许在一个测试中报告多个失败。然而,如果在相关断言失败时继续执行不合理,则应该使用 ASSERT_*。

由于失败的 ASSERT_* 会立即返回当前函数,可能会跳过其后的清理代码,从而可能导致空间泄漏。根据泄漏的性质,如果除了断言错误外还出现堆检查器错误。

要提供自定义的失败消息,只需使用 << 运算符或一系列此类运算符将其流式传递到宏中。

【示例】使用 ASSERT_EQ 和 EXPECT_EQ 宏来验证值的相等性:

ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";for (int i = 0; i < x.size(); ++i) {EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}

任何可以流式传输到 ostream 的内容都可以流式传输到断言宏中 - 特别是 C 字符串字符串对象。如果将宽字符串(wchar_t*、 TCHAR*在 Windows 的UNICODE 模式下,或者 std::wstring)流式传输到断言中,则在打印时会被转换为 UTF-8 编码。

gtest 提供了一系列断言,用于以各种方式验证代码的行为。可以检查布尔条件,基于关系运算符比较值,验证字符串值、浮点值等等。甚至还有一些断言可以通过提供自定义谓词来验证更复杂的状态。


🌍1.2 简单测试

  • 使用 TEST() 宏来定义和命名一个测试函数。这些是普通的 C++ 函数,不返回任何值。
  • 在这个函数中,除了你想包含的有效的 C++ 语句,使用各种 gtest 断言来检查值。
  • 测试结果由断言确定;如果测试中的任何断言失败(无论是致命还是非致命),或者测试崩溃,整个测试都将失败。否则,它成功。
TEST(TestSuiteName, TestName) {... test body ...
}

TEST() 宏的第一个参数是测试套件(test suite)的名称,第二个参数是测试套件内的测试名称。两个名称都必须是有效的 C++ 标识符,并且不能包含下划线【测试的全名=其所属的测试套件+其单独的名称组成。来自不同测试套件的测试可以有相同的单独名称】

【示例】以一个简单的整数函数为例

int Factorial(int n);  // 返回 n 的阶乘

此函数的测试套件可能如下:

// 测试 0 的阶乘
TEST(FactorialTest, HandlesZeroInput) {// 期望 Factorial(0) 的结果是 1EXPECT_EQ(Factorial(0), 1);
}// 测试正数的阶乘
TEST(FactorialTest, HandlesPositiveInput) {// 期望 Factorial(1) 的结果是 1EXPECT_EQ(Factorial(1), 1);// 期望 Factorial(2) 的结果是 2EXPECT_EQ(Factorial(2), 2);// 期望 Factorial(3) 的结果是 6EXPECT_EQ(Factorial(3), 6);// 期望 Factorial(8) 的结果是 40320EXPECT_EQ(Factorial(8), 40320);
}

GoogleTest 按测试套件分组测试结果,因此逻辑上相关的测试应在同一个测试套件中;换句话说,它们的 TEST() 的第一个参数应该相同。

在上面的示例中,我们有两个测试,HandlesZeroInput 和 HandlesPositiveInput,它们属于同一个测试套件 FactorialTest。

在命名你的测试套件和测试时,应该遵循与命名函数和类相同的约定。


🌍1.3 测试夹具:为多个测试使用相同的数据配置

Test Fixture(测试夹具)是指在测试运行前后,需要被执行的代码片段。

如果你发现自己在编写两个或更多操作相似数据的测试,可以使用测试夹具。这样可以为多个不同的测试重复使用相同的对象配置。

创建夹具的步骤:

  1. testing::Test 派生一个类。在类体开始处使用 protected:,因为我们希望从子类访问夹具成员。
  2. 在类中声明你需要使用的任何对象。
  3. 如果需要,编写一个默认构造函数或 SetUp() 函数,为每个测试准备对象。
    一个常见的错误是将 SetUp() 拼写为小写的 Setup() - 在 C++11 中使用 override 确保拼写正确。
  4. 如果需要,编写一个析构函数或 TearDown() 函数来释放你在 SetUp() 中分配的任何资源。
  5. 如果需要,为你的测试定义共享的子程序。
// 定义夹具类
class MyTestFixture : public testing::Test {
protected:// 在这里声明你的对象int* myObject;// 如果需要,编写构造函数或 SetUp() 函数void SetUp() override {myObject = new int(42); // 示例初始化}// 如果需要,编写析构函数或 TearDown() 函数void TearDown() override {delete myObject;}
};// 使用 TEST_F() 进行测试
TEST_F(MyTestFixture, Test1) {// 可以在这里访问 myObjectEXPECT_EQ(*myObject, 42);
}TEST_F(MyTestFixture, Test2) {// 也可以在这里访问 myObjectEXPECT_NE(*myObject, 0);
}

使用夹具时,使用 TEST_F() 而不是 TEST(),因为它允许你访问测试夹具中的对象和子程序:

TEST_F(TestFixtureClassName, TestName) {... test body ...
}

 本文参考GoogleTest 用户指南:GoogleTest User’s Guide | GoogleTest

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/350068.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《华为项目管理之道》第1章笔记

《华为项目管理之道》&#xff0c;是新出的华为官方的项目管理书&#xff0c;整个书不错。第1章的精华&#xff1a; 1.2.2 以项目为中心的机制 伴随着项目型组织的建立&#xff0c;华为逐步形成了完备的项目管理流程和制度&#xff0c;从而将业务运 作构建在项目经营管理之…

MySQL之优化服务器设置(三)

优化服务器设置 InnoDB表空间 InnoDB把数据保存在表空间内&#xff0c;本质上是一个由一个或多个磁盘文件组成的虚拟文件系统。InnoDB用表空间实现很多功能&#xff0c;并不只是存储表和索引。它还保存了回滚日志(旧版本行)、插入缓冲(Insert Buffer)、双写缓冲(Doublewrite …

Python | Leetcode Python题解之第148题排序链表

题目&#xff1a; 题解&#xff1a; class Solution:def sortList(self, head: ListNode) -> ListNode:def merge(head1: ListNode, head2: ListNode) -> ListNode:dummyHead ListNode(0)temp, temp1, temp2 dummyHead, head1, head2while temp1 and temp2:if temp1.v…

深入浅出 Babel:现代 JavaScript 的编译器

在现代前端开发中&#xff0c;JavaScript 的版本更新速度非常快&#xff0c;新的语法和特性层出不穷。然而&#xff0c;旧版本的浏览器并不总是支持这些新特性。为了确保代码的兼容性和稳定性&#xff0c;我们需要一个工具来将现代 JavaScript 代码转换为旧版本的代码。Babel 就…

pdf文件如何防篡改内容

PDF文件防篡改内容的方法有多种&#xff0c;以下是一些常见且有效的方法&#xff0c;它们可以帮助确保PDF文件的完整性和真实性&#xff1a; 加密PDF文档&#xff1a; 原理&#xff1a;通过设置密码来保护PDF文档&#xff0c;防止未经授权的访问和修改。注意事项&#xff1a;密…

【Linux】解锁权限的神秘面纱,让你的系统更安全、更高效!

XShell原理权限 1. Shell命令以及运行原理1.1 Shell外壳1.2 shell周边知识 2. Linux权限的概念2.1 用户2.2 用户切换2.3 sudo 3. Linux权限管理3.1 文件访问者的分类3.2 文件类型3.3 file指令3.4 文件访问权限3.5 文件权限值的表示方法 4. 文件访问权限的设置方法4.1 chmod指令…

【linux】应用程序访问百度时,操作系统内核网络接口日志

代码合入&#xff1a; 登录 - Gitee.comhttps://gitee.com/r77683962/linux-6.9.0/commit/c639573cc7c4984913d4a89884347e5a30a51eac 启动操作系统运行dmesg的日志像这样&#xff1a; dmesg_log/2024_06_14_00_40_54.txt r77683962/linux-6.9.0 - Gitee.com 注意&#xf…

SOFTS: 时间序列预测的最新模型以及Python使用示例

近年来&#xff0c;深度学习一直在时间序列预测中追赶着提升树模型&#xff0c;其中新的架构已经逐渐为最先进的性能设定了新的标准。 这一切都始于2020年的N-BEATS&#xff0c;然后是2022年的NHITS。2023年&#xff0c;PatchTST和TSMixer被提出&#xff0c;最近的iTransforme…

显著提高iOS应用中Web页面的加载速度 - 提前下载页面的关键资源(如JavaScript、CSS和图像)

手动下载并缓存资源是一种有效的方式&#xff0c;可以确保在需要时资源已经在本地存储&#xff0c;这样可以显著提高加载速度。 缓存整个 web 页面的所有资源文件 具体实现步骤 下载和缓存资源&#xff1a;包括 HTML 文件、CSS、JavaScript 和图像。在应用启动时预加载资源。…

WinForm之TCP客户端通讯

目录 一 设计界面 二 后台代码 一 设计界面 二 后台代码 using System.Net.Sockets; using System.Text;namespace TCP网络客户端通讯 {public partial class Form1 : Form{public Form1(){InitializeComponent();}TcpClient tcpClient new TcpClient();private void conne…

停止游戏中的循环扣血显示

停止游戏中循环扣血并显示的具体实现方式会依赖于你的代码结构和游戏的逻辑。通常情况下&#xff0c;你可以通过以下方式来实现停止循环扣血和显示&#xff1a; 1、问题背景 在使用 Python 代码为游戏开发一个生命值条时&#xff0c;遇到了一个问题。代码使用了循环来减少生命…

atcoder abc357

A Sanitize Hands 问题&#xff1a; 思路&#xff1a;前缀和&#xff0c;暴力&#xff0c;你想咋做就咋做 代码&#xff1a; #include <iostream>using namespace std;const int N 2e5 10;int n, m; int a[N];int main() {cin >> n >> m;for(int i 1…

四轴飞行器、无人机(STM32、NRF24L01)

一、简介 此电路由STM32为主控芯片&#xff0c;NRF24L01、MPU6050为辅,当接受到信号时&#xff0c;处理对应的指令。 二、实物图 三、部分代码 void FlightPidControl(float dt) { volatile static uint8_t statusWAITING_1; switch(status) { case WAITING_1: //等待解锁 if…

2024最新最全【AIGC】学习零基础入门到精通,看完这一篇就够了!

这个文案就是由AI生成的哦&#xff01;&#xff01;&#xff01;&#xff01; AIGC&#xff08;AI-Generated Content&#xff09; 即人工智能生成内容&#xff0c;是指利用人工智能技术来创造各种形式的内容&#xff0c;包括文字、图像、视频、音频和游戏等。与专业生成内容…

图解Transformer学习笔记

教程是来自https://github.com/datawhalechina/learn-nlp-with-transformers/blob/main/docs/ 图解Transformer Attention为RNN带来了优点&#xff0c;那么有没有一种神经网络结构直接基于Attention构造&#xff0c;而不再依赖RNN、LSTM或者CNN的结构&#xff0c;这就是Trans…

【算法专题--链表】反转链表II--高频面试题(图文详解,小白一看就会!!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐迭代法 --- 带哨兵位&#xff08;头节点&#xff09; &#x1f95d; 什么是哨兵位头节点&#xff1f; &#x1f34d; 解题思路 四、总结与提炼 五、共勉 一、前言 反转链表II这道题&#xff0c;可以说是--链表专题--&am…

RAG工作流在高效信息检索中的应用

介绍 RAG&#xff08;Retrieval Augmented Generation&#xff09;是一种突破知识限制、整合外部数据并增强上下文理解的方法。 由于其高效地整合外部数据而无需持续微调&#xff0c;RAG的受欢迎程度正在飙升。 让我们来探索RAG如何克服LLM的挑战&#xff01; LLM知识限制大…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第38课-密室逃脱-3D互动剧情

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第38课-密室逃脱 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&…

Flutter - Material3适配

demo 地址: https://github.com/iotjin/jh_flutter_demo 代码不定时更新&#xff0c;请前往github查看最新代码 Flutter - Material3适配 对比图具体实现一些组件的变化 代码实现Material2的ThemeDataMaterial3的ThemeData Material3适配官方文档 flutter SDK升级到3.16.0之后 …

C# WinForm —— 35 StatusStrip 介绍

1. 简介 状态栏 StatusStrip&#xff0c;默认在软件的最下方&#xff0c;用于显示系统时间、版本、进度条、账号、角色信息、操作位置信息等 可以在状态栏中添加的控件类型有&#xff1a;StatusLabel、ProgressBar、DropDownButton、SplitButton 2. 属性 属性解释(Name)控…