Google Test简介

1. 引言

在软件开发过程中,测试是保证代码质量和可靠性的关键环节。Google Test(gtest)是一个由Google开发的C++测试框架,它提供了一套丰富的测试功能,帮助开发者编写和维护高质量的代码。

2. Google Test 基础

2.1 Google Test的安装与配置

Google Test是一个开源项目,可以在GitHub上找到。安装方法取决于你的操作系统和开发环境。以下是一些常见的安装方法:

  • 通过包管理器:许多Linux发行版和macOS的Homebrew支持通过包管理器安装gtest。

    sudo apt-get install libgtest-dev  # Debian/Ubuntu
    brew install googletest         # macOS
    
  • 从源代码编译:克隆gtest的仓库,并使用CMake构建和安装。

    git clone https://github.com/google/googletest.git
    cd googletest
    cmake .
    make
    sudo make install
    

安装完成后,确保在编译测试代码时链接gtest库。如果你使用的是CMake,可以在CMakeLists.txt文件中添加以下内容:

find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})

2.2 编写第一个测试用例

创建一个简单的测试用例来验证gtest的基本用法。首先,定义一个函数,然后编写一个测试这个函数的测试用例。

// example.h
#ifndef EXAMPLE_H
#define EXAMPLE_Hint Add(int a, int b);#endif // EXAMPLE_H
// example.cc
#include "example.h"int Add(int a, int b) {return a + b;
}
// example_test.cc
#include "example.h"
#include "gtest/gtest.h"TEST(AdditionTest, HandlesPositiveNumbers) {EXPECT_EQ(Add(2, 3), 5);
}TEST(AdditionTest, HandlesNegativeNumbers) {EXPECT_EQ(Add(-1, -1), -2);
}int main(int argc, char **argv) {::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}

2.3 测试用例的组织结构

测试用例应该根据被测试的功能模块进行组织。例如,如果你的项目包含多个类或模块,每个类或模块应该有其对应的测试文件。

  • 按功能模块组织:每个模块或类有自己的测试文件。
  • 命名规范:测试文件和测试用例的命名应该清晰表达它们测试的对象和功能。

以下是一个更复杂的示例,展示如何组织多个测试用例:

// calculator.h
#ifndef CALCULATOR_H
#define CALCULATOR_Hclass Calculator {
public:int Add(int a, int b);int Subtract(int a, int b);// 其他操作...
};#endif // CALCULATOR_H
// calculator_test.cc
#include "calculator.h"
#include "gtest/gtest.h"class CalculatorTest : public ::testing::Test {
protected:Calculator calc;void SetUp() override {// 在每个测试用例执行前设置}void TearDown() override {// 在每个测试用例执行后清理}
};TEST_F(CalculatorTest, TestAdd) {EXPECT_EQ(calc.Add(2, 3), 5);
}TEST_F(CalculatorTest, TestSubtract) {EXPECT_EQ(calc.Subtract(5, 3), 2);
}// 可以添加更多的测试用例...

在这个示例中,我们使用了TEST_F宏来定义一个测试套件,它允许我们在每个测试用例执行前后使用SetUpTearDown方法进行环境的设置和清理。

3. 测试用例的编写

编写测试用例是确保代码质量的关键步骤。在本节中,我们将深入探讨如何编写有效的测试用例,包括命名规范、断言的使用,以及参数化测试。

3.1 测试用例的命名规范

测试用例的命名应该清晰、直观,能够快速传达测试的目的和范围。以下是一些命名规范的建议:

  • 使用大写字母和下划线分隔单词。
  • 测试用例名称应以Test开头,后跟被测试的功能和测试场景。
  • 对于边界条件或特殊情况,使用明确的后缀,如TestBoundaryCondition

例如,如果你正在测试一个字符串反转函数,测试用例的名称可能是TestStringReverse_EmptyStringTestStringReverse_WithSpecialCharacters

3.2 使用ASSERT和EXPECT宏

Google Test提供了两种断言宏:ASSERT_*EXPECT_*。它们的主要区别在于,当断言失败时,ASSERT_*会终止当前测试,而EXPECT_*则允许测试继续执行。

  • ASSERT系列:用于关键检查,一旦失败,测试立即终止。

    ASSERT_EQ(5, Add(2, 3)); // 如果不相等,测试终止
    
  • EXPECT系列:用于非关键检查,即使失败,测试也会继续执行。

    EXPECT_EQ(5, Add(2, 3)); // 如果不相等,测试继续
    

使用EXPECT_*宏可以提供更多的错误信息,因为它允许测试继续执行,从而可以捕获更多的断言失败。

3.3 测试参数化

参数化测试允许你为单个测试用例提供多种输入值,而无需编写多个测试函数。这在测试算法的正确性或性能时非常有用。

以下是如何使用gtest的参数化测试:

  1. 定义参数化测试结构
class MathTest : public ::testing::TestWithParam<int> {};
  1. 实例化测试套件
TEST_P(MathTest, Add) {int a = GetParam();EXPECT_EQ(a + 1, Add(a, 1));
}INSTANTIATE_TEST_SUITE_P(Addition, MathTest, ::testing::Range(0, 10));

在这个例子中,MathTest是一个参数化测试类,它接受一个整数参数。TEST_P宏用于定义测试用例,INSTANTIATE_TEST_SUITE_P宏用于实例化测试套件,这里我们使用Range函数生成从0到9的参数。

3.4 测试用例的组织

测试用例应该按照模块或功能进行组织,以便于管理和维护。以下是一些组织测试用例的建议:

  • 按模块划分:每个模块或类有自己的测试文件。
  • 使用命名空间:使用命名空间来组织不同模块的测试代码。
  • 避免重复代码:使用工厂函数或测试工具类来减少重复代码。

3.5 示例:一个完整的参数化测试

假设我们有一个简单的数学库,包含加法和乘法函数,我们想要测试这些函数的参数化性能。

// math_functions.h
#ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_Hint Add(int a, int b);
int Multiply(int a, int b);#endif // MATH_FUNCTIONS_H
// math_functions_test.cc
#include "math_functions.h"
#include "gtest/gtest.h"
#include "gtest/gtest-param-test.h"class MathFunctionTest : public ::testing::TestWithParam<int> {};TEST_P(MathFunctionTest, TestAdd) {int a = GetParam();EXPECT_EQ(a + 1, Add(a, 1));
}TEST_P(MathFunctionTest, TestMultiply) {int a = GetParam();EXPECT_EQ(a * 2, Multiply(a, 2));
}INSTANTIATE_TEST_SUITE_P(SingleDigit, MathFunctionTest, ::testing::Range(0, 10));int main(int argc, char **argv) {::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}

在这个示例中,我们定义了一个参数化测试类MathFunctionTest,它将测试加法和乘法函数。我们使用INSTANTIATE_TEST_SUITE_P宏来为0到9的整数范围实例化测试。

4. 高级特性

在掌握gtest的基本用法之后,我们可以探索一些高级特性,这些特性可以帮助我们更有效地编写和组织测试代码。

4.1 测试套件(Test Suites)

测试套件是将一组相关的测试用例组织在一起的方式,它们可以共享设置和清理代码。使用TEST宏定义的测试用例默认属于全局测试套件。但是,你可以使用TEST_F宏定义基于::testing::Test的子类的测试套件。

class MyTestSuite : public ::testing::Test {
protected:void SetUp() override {// 测试套件的设置代码}void TearDown() override {// 测试套件的清理代码}
};TEST_F(MyTestSuite, TestA) {// ...
}TEST_F(MyTestSuite, TestB) {// ...
}

4.2 测试过滤

gtest允许你通过命令行参数过滤要运行的测试,这在调试或仅运行特定测试时非常有用。

  • 使用--gtest_filter=<Pattern>来过滤测试。模式可以是测试套件或测试用例的名称。
  • 使用--gtest_also_run_disabled_tests来运行被禁用的测试。

例如,要运行所有包含MyTestSuite的测试,可以使用以下命令:

./your_test_binary --gtest_filter=MyTestSuite.*

4.3 环境设置(SetUp和TearDown)

在每个测试用例执行前后,你可能需要执行一些设置或清理工作。SetUpTearDown方法提供了一个方便的方式来执行这些操作。

class MyTestSuite : public ::testing::Test {
protected:void SetUp() override {// 初始化资源,如数据库连接、文件句柄等}void TearDown() override {// 清理资源}
};

4.4 测试禁用

有时,你可能需要临时禁用某些测试,而不是删除它们。gtest允许你通过在测试用例名称前添加DISABLED_前缀来禁用测试。

TEST(MyTestSuite, DISABLED_TestC) {// 这个测试当前被禁用
}

4.5 测试属性

gtest允许你给测试用例添加属性,这可以用于进一步的测试分类和过滤。

GTEST_DEFINE_bool(also_run_disabled_tests, false,"Also run disabled tests.");TEST(MyTestSuite, TestD) {// 这个测试有一个属性
}TEST(MyTestSuite, TestE) {// 这个测试没有属性
}int main(int argc, char **argv) {::testing::InitGoogleTest(&argc, argv);if (::testing::FLAGS_also_run_disabled_tests) {::testing::GTEST_FLAG(also_run_disabled_tests) = true;}return RUN_ALL_TESTS();
}

4.6 测试事件监听器

gtest提供了一个事件监听器接口,允许你监视测试执行过程中的事件,如测试开始、结束等。

class MyEventListener : public ::testing::EmptyTestEventListener {
public:void OnTestStart(const ::testing::TestInfo& test_info) override {// 测试开始时的回调}void OnTestEnd(const ::testing::TestInfo& test_info) override {// 测试结束时的回调}
};int main(int argc, char **argv) {::testing::InitGoogleTest(&argc, argv);::testing::TestEventListeners& listeners = ::testing::UnitTest::GetInstance()->listeners();listeners.Append(new MyEventListener);return RUN_ALL_TESTS();
}

4.7 示例:一个完整的测试套件

让我们将上述概念综合到一个示例中,创建一个包含多个测试用例和高级特性的测试套件。

// my_test_suite.h
#ifndef MY_TEST_SUITE_H
#define MY_TEST_SUITE_H#include "gtest/gtest.h"class MyTestSuite : public ::testing::Test {
protected:int resource;void SetUp() override {resource = 0;}void TearDown() override {// 假设resource需要特别的清理}
};#endif // MY_TEST_SUITE_H
// my_test_suite.cc
#include "my_test_suite.h"TEST_F(MyTestSuite, TestResourceInitialization) {EXPECT_EQ(resource, 0);resource = 1;
}TEST_F(MyTestSuite, TestResourceUsage) {EXPECT_EQ(resource, 1);// 测试使用resource的逻辑
}// 禁用的测试
TEST_F(MyTestSuite, DISABLED_TestResourceFailure) {// 这个测试当前被禁用
}int main(int argc, char **argv) {::testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}

5. 测试结果的输出与分析

测试结果的输出与分析是理解测试执行情况和识别问题的关键环节。Google Test 提供了灵活的测试结果报告机制,允许开发者根据需要定制测试结果的输出格式和内容。

5.1 测试结果的输出格式

Google Test 默认会输出一个简洁的测试结果报告,包括测试用例的名称、执行状态(通过、失败或跳过)以及失败测试的错误信息。此外,gtest 还支持多种输出格式:

  • 正常输出:控制台输出,显示测试用例的执行结果。
  • XML输出:通过 --gtest_output 参数设置,输出 JUnit 风格的 XML 报告,适用于持续集成系统。

例如,要生成 XML 格式的测试报告,可以使用以下命令行参数:

./your_test_binary --gtest_output="xml:output_directory/"

这将在指定的目录下生成一个 XML 文件,包含了所有测试结果的详细信息。

5.2 测试结果的分析方法

分析测试结果是一个重要的步骤,它可以帮助你理解测试的覆盖情况和发现潜在的问题。

  • 检查失败的测试:分析失败的测试用例,找出失败的原因。
  • 查看日志输出:gtest 允许你在测试代码中输出日志信息,这可以帮助你了解测试执行过程中的状态。
  • 使用测试覆盖率工具:结合测试覆盖率工具(如 gcov 或 lcov)来分析测试覆盖情况。

5.3 集成到持续集成系统

将 Google Test 集成到持续集成(CI)系统中,可以自动化测试流程,确保代码的持续健康。

  • 配置 CI 系统:大多数 CI 系统都支持运行 Google Test 测试套件,并收集测试结果。
  • 自动化测试:设置 CI 系统在代码提交或定期间隔自动运行测试。
  • 收集和报告结果:CI 系统可以收集测试结果,并提供详细的测试报告。

5.4 示例:生成和解析 XML 测试报告

下面是一个示例,展示如何生成 XML 格式的测试报告,并使用 Python 脚本来解析这些报告。

首先,确保在编译测试时链接了gtest的 XML 报告生成功能:

find_package(GTest REQUIRED)
enable_testing()
add_executable(my_test my_test.cc)
target_link_libraries(my_test GTest::GTest GTest::Main)

然后,运行测试并生成 XML 报告:

./my_test --gtest_output="xml:my_test_results.xml"

接下来,使用 Python 脚本解析 XML 报告:

import xml.etree.ElementTree as ET# 解析 XML 文件
tree = ET.parse('my_test_results.xml')
root = tree.getroot()# 遍历测试套件
for suite in root.findall('testsuites/testsuite'):suite_name = suite.get('name')print(f"Test Suite: {suite_name}")for case in suite.findall('testcase'):case_name = case.get('name')status = 'Passed' if case.find('failure') is None else 'Failed'print(f"  {case_name}: {status}")

这个 Python 脚本会读取 XML 报告,并打印出每个测试用例的状态。

5.5 测试结果的高级分析

对于更高级的测试结果分析,你可以使用以下方法:

  • 性能分析:使用 gtest 的 --gtest_print_time 选项来输出每个测试用例的执行时间。
  • 跨多个构建的比较:收集不同构建的测试结果,使用工具进行比较分析。
  • 可视化测试结果:使用测试结果可视化工具,如 TestNG 的 HTML 报告,来更直观地展示测试结果。

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

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

相关文章

2024年护网行动全国各地面试题汇总(4)作者:————LJS

面试过程及回答 自我介绍这里就如实回答的工作经历&#xff0c;参与的项目&#xff0c;尽量简短的把你参与的项目和成果说出来就行 使用过哪些设备&#xff0c;出现误报怎么办 天眼、EDR、全流量告警、态势感知、APT、蜜罐设备先去查看设备的完整流量日志等信息确认是否为误报&…

PS的抠图算法原理剖析 2

记得还有个这种抠图方式 我们尝试理解一下 第一步&#xff0c;人工选取点位&#xff0c;然后其实构造了一个凸多边形&#xff0c;这一步还行&#xff0c;中间有洞就挖洞&#xff0c;类似earcut那种东西 第二步&#xff0c;2个点中间的连线部分&#xff0c;怎么包裹到轮廓的&a…

单调队列——Acwing.154滑动窗口

单调队列 定义 单调队列是一个限制只能队尾插入&#xff0c;但是可以两端删除的双端队列。单调队列存储的元素值&#xff0c;是从队首到队尾单调递增或单调递减的。 运用情况 滑动窗口最大值&#xff1a;给定一个整数数组和一个窗口大小&#xff0c;计算窗口内的最大值。任…

idea 新建一个 JSP(JavaServer Pages)项目

环境设置&#xff1a; 确保你的开发环境中已经安装了 Java 开发工具包&#xff08;JDK&#xff09;和一个 Java Web 开发的集成开发环境&#xff08;IDE&#xff09;&#xff0c;比如 Eclipse、IntelliJ IDEA 或者 NetBeans。你还需要一个 Web 服务器&#xff0c;比如 Apache T…

深入解析MySQL的层次化设计

一、基础架构 1.连接器 1.会先连接到这个数据库上&#xff0c;这时候接待你的就是连接器。连接器负责跟客户端建立连接、获取权限、维持和管理连接 2.用户密码连接成功之后&#xff0c;会从权限表中拿出你的权限&#xff0c;后续操作权限都依赖于此时拿出的权限,这就意味着当链…

达梦导入导出

针对导出数据库表结构通常有 3 种方法&#xff1a; 使用 DTS 导出 打开 DTS 迁移工具&#xff0c;选择【DM-->SQL】并链接到数据库中&#xff0c;如下图所示&#xff1a; 添加定义脚本&#xff0c;并选择【迁移范围】&#xff08;仅迁移对象定义&#xff09;&#xff0c;如…

不愧是字节,图像算法面试真细致

这本面试宝典是一份专为大四、研三春招和研二暑假实习生准备的珍贵资料。 涵盖了图像算法领域的核心知识和常见面试题&#xff0c;包括卷积神经网络、实例分割算法、目标检测、图像处理等多个方面。不论你是初学者还是有经验的老手&#xff0c;都能从中找到实用的内容。 通过…

内存卡提示需要格式化?别急,这样拯救你的数据

一、内存卡突然提示需要格式化 在日常生活中&#xff0c;我们经常会使用到内存卡来存储照片、视频、文档等重要数据。然而&#xff0c;有时当我们试图访问内存卡时&#xff0c;却会遭遇一个令人头疼的问题——系统突然提示“内存卡需要格式化”。这意味着我们无法直接读取或写…

如何利用AI绘图,生成同一人物(最好为二次元)的不同动作和不同表情的图片?

Ai绘画有一个很现实的问题&#xff0c;要保证每次画出的都是同一个人物的话&#xff0c;很费劲。 Midjourney就不必说了&#xff0c;人物的高度一致性一直得不到很好的解决。而在Stable Diffusion&#xff08;SD&#xff09;中&#xff0c;常用办法是通过同一个Seed值&#xf…

查询ubuntu的ip方法

命令如下 #这个命令比较详细 ip addr show enp1s0 #enp1s0是网卡名#这个也详细 nmcli device show enp1s0如果发现同个网卡出现多个ip&#xff0c;则可以删除其中一个ip sudo ip addr del 192.168.1.80/24 dev enp1s0重启网络管理 sudo systemctl restart NetworkManager查询…

汇凯金业:黄金期货交易时间规则

黄金期货交易时间规则因交易所不同而有所差异。以下是几个主要交易所的黄金期货交易时间及其相关规则&#xff1a; 一、纽约商品交易所(COMEX) 纽约商品交易所(COMEX)是全球最大的黄金期货交易市场之一&#xff0c;其黄金期货交易时间如下&#xff1a; 电子交易时间(通过CME…

Linux 服务器 CUDA两版本

使用nvidia-smi命令查看CUDA版本为11.4&#xff0c;nvcc -V命令查看CUDA版本为11.1。以nvcc -V版本为准。 CUDA有两个CUDA版本 runtime api 使用nvcc -V显示 &#xff0c;是运行版 driver api&#xff0c; 使用nvidia-smi显示&#xff0c;是驱动版 如果报错的命令是RuntimeErr…

黑苹果睡眠总是自动唤醒(RTC)

黑苹果睡眠总是自动唤醒【RTC】 1. 问题2. 解决方案2.1. 查看重启日志2.2. 配置Disable RTC wake scheduling补丁 3. 后续4. 参考 1. 问题 黑苹果EFI 更换后&#xff0c;总是在手动 睡眠后&#xff0c;间歇性重启&#xff0c;然后再次睡眠&#xff0c;然后再重启。原因归结为&…

【RAG】RAG性能提升之路-RAPTOR:一种构建递归文档树的增强检索方法

背景 检索增强型语言模型&#xff08;RALMs&#xff09;在处理需要不断更新的知识和大量信息的文档时确实展现出了优势。然而&#xff0c;现有的方法在处理长篇文档时存在局限性&#xff0c;主要是因为它们通常只能检索较短的文本片段&#xff0c;这限制了对整体文档上下文的全…

C# WPF入门学习主线篇(二十八)—— 使用集合(ObservableCollection)

C# WPF入门学习主线篇&#xff08;二十八&#xff09;—— 使用集合&#xff08;ObservableCollection&#xff09; 在WPF中&#xff0c;数据绑定是构建动态和响应式用户界面的关键。ObservableCollection是一个特别有用的集合类型&#xff0c;它不仅支持数据绑定&#xff0c;还…

同三维T80005EHS-4K60 4K60 HDMI/SDI编码器

1路4K60 HDMI或12G SDI输入&#xff0c;2路3.5MM音频输入&#xff0c;对应HDMI或SDI&#xff0c;1个USB口和1个SD卡槽&#xff0c;可录像到U盘/移动硬盘/SSD硬盘/TF卡 产品简介&#xff1a; 同三维T80005EHS-4K60 4K60HDMI/SDI H.265编码器采用最新高效H.265高清数字视频压缩…

解答 | 有免费的IP地址证书吗

在当今网络安全至关重要的时代&#xff0c;SSL/TLS证书已成为保障数据传输安全的基础。这些证书确保了客户端与服务器之间通信的加密&#xff0c;防止数据被窃取或篡改。尽管如此&#xff0c;当涉及到为IP地址而非域名颁发证书时&#xff0c;情况就变得复杂了。不同于域名验证证…

数据库管理-第203期 阻碍数据库国产化前行的硬件(20240614)

数据库管理203期 2024-06-14 数据库管理-第203期 阻碍数据库国产化前行的硬件&#xff08;20240614&#xff09;1 有但不是全有2 满足部分也是满足3 晚而慢总结 数据库管理-第203期 阻碍数据库国产化前行的硬件&#xff08;20240614&#xff09; 作者&#xff1a;胖头鱼的鱼缸&…

JavaFX:使用appendText自动滚动TextArea,但不使用侦听器

JavaFX是一种用于构建富客户端应用程序的Java库。它提供了丰富的图形化用户界面&#xff08;GUI&#xff09;组件和功能&#xff0c;使开发人员能够创建具有吸引力和交互性的应用程序。 在JavaFX中&#xff0c;TextArea是一个用于显示和编辑多行文本的控件。要实现使用appendT…

Python+Opencv是实现车牌自动识别

使用Python和OpenCV实现车牌自动识别(车牌识别系统,LPR, License Plate Recognition)是一个复杂的任务,通常涉及多个步骤,包括图像预处理、车牌定位、字符分割和字符识别。以下是一个简化的步骤概述以及如何使用OpenCV和其他库(如Tesseract OCR)来实现这些步骤: 图像预…