news 2026/9/25 10:26:14

Robomongo 内置 Google Test 1.8.1:C++ 单元测试框架集成与实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Robomongo 内置 Google Test 1.8.1:C++ 单元测试框架集成与实战指南
  • 数据库客户端
  • 桌面应用

【免费下载链接】robomongo

Native cross-platform MongoDB management tool

项目地址:https://gitcode.com/gh_mirrors/ro/robomongo
点击查看免费下载

Google Test(googletest)是 Google 开源的 C++ 测试框架,本仓库将其 1.8.1 版本以第三方依赖的形式完整内置于src/third-party/googletest-1.8.1/,并通过 CMake 接入 Robomongo(Robo 3T 的原生跨平台 MongoDB 管理工具)的构建体系。本文以该框架的 README 文档为主体,结合仓库内的构建脚本与真实测试用例,讲解 googletest 的核心特性、平台要求、三种主流构建方式,以及如何像 Robomongo 一样用它为 C++ 代码编写并运行单元测试。

Google Test 是什么:Google 的 C++ 测试框架

src/third-party/googletest-1.8.1/README.md开篇即点明:本仓库是原先彼此独立的GoogleTest与GoogleMock两个项目的合并产物——两者关系过于紧密,因此合并维护、一并发布。GoogleTest 是核心测试框架,GoogleMock 则是其扩展,用于编写和使用 C++ mock 类,其独立文档见 googlemock/README.md。

该 README 还披露了框架版本演进路线(属于项目自述,非当前仓库承诺):

  • 1.8.x:将是最后一个支持 pre-C++11 编译器的版本,此后不再接受新功能请求,仅受理被证明"关键"的 bug 修复;
  • 1.9.x:1.8.x 之后的工作重点是清理技术债务并打上 1.9.x 标签;
  • 1.9.x 之后:googletest 将遵循 Abseil 的 "Live at Head" 哲学。

本仓库固定使用的是1.8.1版本,在根 CMakeLists.txt 中通过set(GOOGLE_TEST_VERSION 1.8.1)显式声明,并对应set(GOOGLE_TEST_DIR src/third-party/googletest-${GOOGLE_TEST_VERSION})、add_subdirectory(${GOOGLE_TEST_DIR})纳入构建,版本号还会作为编译期宏GOOGLE_TEST_VERSION注入到主程序(见 src/robomongo/CMakeLists.txt)。

核心功能特性一览

README 归纳了 googletest 的能力清单,这些特性也正是 Robomongo 用它组织测试的基础:

  • xUnit 测试框架:与 JUnit/PyUnit 同构的经典架构;
  • 测试发现(Test discovery):TEST()/TEST_F()注册的测试会被框架自动发现,无需手工枚举;
  • 丰富的断言(Assertions):覆盖布尔、数值比较、字符串比较等场景;
  • 用户自定义断言:可扩展框架自身的断言体系;
  • 死亡测试(Death tests):用于验证进程崩溃类行为;
  • 致命与非致命失败:ASSERT_*与EXPECT_*两套失败语义;
  • 值参数化测试(Value-parameterized tests):同一测试逻辑跑多组输入;
  • 类型参数化测试(Type-parameterized tests):同一测试逻辑跑多种类型;
  • 多种运行选项:通过命令行 flag 控制测试程序行为;
  • XML 测试报告生成:输出机器可读的测试结果。

支持的平台与构建要求

README 明确列出 googletest 已实际使用的平台:Linux、Mac OS X、Windows、Cygwin、MinGW、Windows Mobile、Symbian。当前官方积极支持的构建环境为 Linux、Windows、Mac OS X 与 Cygwin,其他平台(如 Solaris、AIX、z/OS)属于尽力而为。

各平台的最小构建要求如下:

平台要求
LinuxGNU 兼容的 make/gmake、POSIX 标准 shell、POSIX(-2) 正则库(regex.h)、C++98 标准编译器
WindowsMicrosoft Visual C++ 2015 或更新版本
CygwinCygwin v1.5.25-14 或更新版本
Mac OS XMac OS X v10.4 Tiger 或更新版本、Xcode Developer Tools

需要注意的是,框架本身的"最低要求"(C++98)与 Robomongo 主工程并不相同——主工程已在根 CMakeLists.txt 中启用CMAKE_CXX_STANDARD 17,因此实际编译测试代码时遵循的是 C++17 标准。

在 Robomongo 中的集成:CMake 与目录结构

googletest 1.8.1 在仓库中的目录布局为:

src/third-party/googletest-1.8.1/ ├── googletest/ # 核心测试框架(含 include、src、docs、make、msvc、xcode 等) ├── googlemock/ # mock 扩展(含 include、src、docs、scripts 等) ├── CMakeLists.txt ├── CONTRIBUTING.md └── README.md

README 指出,更详细的构建说明在其内部文档 googletest/README.md 中;入门概念则见 googletest/docs/primer.md。下文三种构建方式均出自这份内部构建文档。

方式一:通用手工构建(Generic Build)

假设 googletest 位于${GTEST_DIR},把src/gtest-all.cc编译进库目标即可——${GTEST_DIR}/include放入系统头文件搜索路径,${GTEST_DIR}放入普通头文件搜索路径。Linux/gcc 下等价于:

g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \ -pthread -c ${GTEST_DIR}/src/gtest-all.cc ar -rv libgtest.a gtest-all.o

-pthread不可省略,因为 googletest 内部使用了线程。随后编译自己的测试源文件并链接 gtest:

g++ -isystem ${GTEST_DIR}/include -pthread path/to/your_test.cc libgtest.a \ -o your_test

对应的源文件正是本仓库中的 googletest/src/gtest-all.cc。make/目录还附带一个基于 GNU make 的 Makefile,在默认环境匹配时,make && ./sample1_unittest即可产出库与示例测试。

方式二:独立 CMake 工程构建

googletest 自带跨平台 CMake 脚本("C" 即 cross-platform),可生成你所在平台的本地构建文件:

mkdir mybuild # 创建构建输出目录 cd mybuild cmake ${GTEST_DIR} # 生成原生构建脚本

如需连框架自带的 samples 一起构建,将最后一条命令替换为cmake -Dgtest_build_samples=ON ${GTEST_DIR}。随后:类 Unix 系统下出现 Makefile,make即可;Windows + Visual Studio 下生成gtest.sln与若干.vcproj;Mac OS X + Xcode 下生成.xcodeproj。

方式三:并入既有 CMake 工程(Robomongo 采用的方式)

对于一个已经使用 CMake 的工程,更健壮的做法是直接作为主工程的一部分构建 gtest——这样 gtest 与主工程共用同一套编译器与链接器设置,可规避 debug/release 等不兼容库引发的问题,在 Windows 上尤其有用。googletest/README.md 给出了四种让源码进入主构建的途径:手工下载放到固定位置、直接内嵌进主工程源码树、以 git submodule 引入、在 CMake configure 阶段联网下载。

其中"configure 阶段下载"的实现要点是:用一个单独的CMakeLists.txt.in(cmake_minimum_required(VERSION 2.8.2),通过ExternalProject_Add定义 googletest 的下载与构建元数据),在主工程里依次执行configure_file、execute_process(COMMAND ${CMAKE_COMMAND} -G ...)、execute_process(COMMAND ${CMAKE_COMMAND} --build .),最后用add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src ${CMAKE_BINARY_DIR}/googletest-build EXCLUDE_FROM_ALL)拉入构建,并链接gtest/gtest_main目标。此外文档还提醒:若 CMake 版本低于 2.8.11,需要手工include_directories("${gtest_SOURCE_DIR}/include"),因为头文件搜索路径的自动传递是从 2.8.11 才开始支持的。

本仓库实际选择了"源码内嵌 +add_subdirectory"的路线:根 CMakeLists.txt 依次add_subdirectory各第三方库后,再add_subdirectory(src/robomongo)与add_subdirectory(src/robomongo-unit-tests)。

补充:Visual Studio 运行库(Runtime Library)不匹配

googletest/README.md 专门警告了一个 Windows 常见坑:新版 VS 工程默认动态链接 C 运行库,而 gtest 默认静态链接,会报LNK2038: mismatch detected for 'RuntimeLibrary'。框架为此提供 CMake 选项gtest_force_shared_crt,开启后 gtest 也动态链接运行库,从而与宿主工程匹配。Robomongo 在 src/robomongo-unit-tests/CMakeLists.txt 中即设置了set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)。

Robomongo 单元测试工程实战

Robomongo 的单元测试工程位于src/robomongo-unit-tests/,其构建脚本与目录 README 共同定义了测试的组织规范。

测试文件命名约定

src/robomongo-unit-tests/README.md 说明:该目录本身只是 CMake 的占位符,测试源文件与被测文件同目录存放、并肩出现,命名形如:

RoboCrypt.cpp # 被测源码 RoboCrypt_test.cpp # 对应测试 StringOperations.cpp StringOperations_test.cpp

即测试文件放在src/robomongo/及其子目录中,文件名以_test.cpp结尾。cmake/的 RobomongoCommon.cmake 等模块与src/robomongo-unit-tests/CMakeLists.txt中的注释共同印证了这一约定:"New test & source file pairs MUST have the following format:Test file: /path/Foo_test.cpp、Source file: /path/Foo.cpp or /path/Foo.h"。

单元测试的 CMake 配置拆解

src/robomongo-unit-tests/CMakeLists.txt 完整展示了 googletest 的接入方式:

  1. Linux 特例:脚本开头if (SYSTEM_LINUX) ... return() endif(),明确注明"Currently unit testing is disabled for Linux due to MongoDB linking problems"(因 MongoDB 链接问题,Linux 上当前禁用单元测试)。因此robo_unit_tests目标实际在 macOS 与 Windows 上构建;
  2. 启用 gtest:enable_testing()、include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}),并强制gtest_force_shared_crt;
  3. 登记测试源:SOURCES_TEST依次列出 RoboCrypt_test.cpp、StringOperations_test.cpp、HexUtils_test.cpp;
  4. 生成可执行目标:add_executable(robo_unit_tests ${SOURCES_TEST}),并add_dependencies(robo_unit_tests robomongo)保证主目标先构建;
  5. 复用主程序目标文件:通过get_target_property(ROBO_SOURCES robomongo SOURCES)取出 robomongo 的全部源码,过滤掉main.cpp后,按平台拼出对应.obj/.cpp.o路径,作为ROBO_OBJ_FILES链入测试程序——这是"直接测试真实业务代码、不做桩"的关键设计;
  6. 链接依赖:target_link_libraries(robo_unit_tests gtest gtest_main Qt5::Widgets Qt5::Network Qt5::Xml qjson qscintilla mongodb ssh Threads::Threads ${ROBO_OBJ_FILES})——注意同时链入了gtest与gtest_main,后者提供现成的main();
  7. Windows 运行环境:把robo_unit_tests所需的 Qt5 DLL 与 OpenSSL 的libssl-1_1-x64.dll、libcrypto-1_1-x64.dll拷贝到测试二进制目录,保证可独立运行。

macOS 分支还会额外链接Security、CoreFoundation与-lresolv(见脚本第 57-60 行)。

三个真实测试用例解读

字符串工具——StringOperations_test.cpp 是最简洁的TEST()用例,文件头注释还给出了本工程的命名风格建议(UnitOfWorkName_ScenarioUnderTest_ExpectedBehavior):

#include "gtest/gtest.h" #include "robomongo/utils/StringOperations.h" TEST(StringOperationsTests, captilizeFirstChar) { // EXPECT_EQ("Abcc", Robomongo::captilizeFirstChar("abc")); // Simulating failing test EXPECT_EQ("Abc", Robomongo::captilizeFirstChar("abc")); // Simulating passing test }

被测函数Robomongo::captilizeFirstChar声明于 src/robomongo/utils/StringOperations.h。

加解密往返——RoboCrypt_test.cpp 对密码存储模块 RoboCrypt.h 做加密-解密往返校验,覆盖了含转义、标点、符号的多种密码形态,EXPECT_EQ(pwd, decryptedPwd)验证一致性:

TEST(RoboCrypt_CoreTests, encrypt_decrypt) { auto const pwds = { "Tyu_aBq", "_?asdfghjkl;'piop[.,/", ".?/`_@~!#$%^^&&*)_)_+=-", "<>?/.,;':][p{}|\"" }; for (auto const& pwd : pwds) { const std::string encryptedPwd = Robomongo::RoboCrypt::encrypt(pwd); const std::string decryptedPwd = Robomongo::RoboCrypt::decrypt(encryptedPwd); EXPECT_EQ(pwd, decryptedPwd); } }

十六进制判定——HexUtils_test.cpp 用EXPECT_TRUE(Robomongo::HexUtils::isHexString("a"));验证工具函数的布尔返回值。

这三个文件清晰展示了 googletest 在真实项目中的典型形态:#include "gtest/gtest.h"+TEST(TestCaseName, TestName)+ 断言宏,无需自写main()(由gtest_main提供)。

编写测试的核心 API(来自 Primer)

googletest/docs/primer.md 是官方入门文档,下面提炼其核心内容,与仓库测试用例一一对应。

断言:ASSERT_* 与 EXPECT_*

断言是检查条件是否为真的语句,结果分为成功、非致命失败、致命失败三种;致命失败会中止当前函数。断言成对出现:ASSERT_*失败即致命并中止当前函数,EXPECT_*失败为非致命、不中止函数。惯例是优先EXPECT_*(一次运行能报出多个失败),只有"继续运行没有意义"时才用ASSERT_*。

自定义失败消息通过<<流入宏:

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; }

基本断言(Linux、Windows、Mac 均可用):

致命断言非致命断言验证内容
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_EQ对指针做的是指针相等判断,比较两个 C 字符串是否同内容应改用ASSERT_STREQ;指针与空值比较推荐nullptr(ASSERT_EQ(ptr, nullptr));实参只求值一次,因此允许带副作用,但求值顺序不保证;浮点比较建议使用 advanced 指南中的浮点专用变体。

C 字符串比较断言:

致命断言非致命断言验证内容
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);忽略大小写后内容相同
ASSERT_STRCASENE(str1, str2);EXPECT_STRCASENE(str1, str2);忽略大小写后内容不同

注意"NULL 指针与空字符串被视为不同";比较std::string对象请用EXPECT_EQ系列而非*STREQ*。

简单测试:TEST() 宏

创建测试三步走:用TEST()定义命名测试函数(普通无返回值 C++ 函数)→ 函数体内用断言检查 → 任一断言失败或崩溃则整个测试失败。宏签名为TEST(TestCaseName, TestName),参数从一般到具体,两个名字都必须是合法 C++ 标识符且不应包含下划线。官方给出的阶乘示例:

TEST(FactorialTest, HandlesZeroInput) { EXPECT_EQ(Factorial(0), 1); } TEST(FactorialTest, HandlesPositiveInput) { EXPECT_EQ(Factorial(1), 1); EXPECT_EQ(Factorial(2), 2); EXPECT_EQ(Factorial(3), 6); EXPECT_EQ(Factorial(8), 40320); }

同一测试用例(第一个参数相同)的测试在结果报告中归组展示。Robomongo 的TEST(StringOperationsTests, captilizeFirstChar)与TEST(RoboCrypt_CoreTests, encrypt_decrypt)正是该模式的应用。

测试夹具(Test Fixtures):TEST_F() 宏

当多个测试共享同一份对象配置时,使用夹具类:从::testing::Test派生,protected:起始,声明成员对象;需要时实现默认构造函数或SetUp()(注意拼写,C++11 下用override校验)、析构函数或TearDown()。使用TEST_F(TestCaseName, TestName),此时第一个参数必须是夹具类名(_F即 Fixture)。

官方以 FIFO 队列Queue为例:

class QueueTest : public ::testing::Test { protected: void SetUp() override { q1_.Enqueue(1); q2_.Enqueue(2); q2_.Enqueue(3); } Queue<int> q0_; Queue<int> q1_; Queue<int> q2_; }; TEST_F(QueueTest, IsEmptyInitially) { EXPECT_EQ(q0_.size(), 0); } TEST_F(QueueTest, DequeueWorks) { int* n = q0_.Dequeue(); EXPECT_EQ(n, nullptr); n = q1_.Dequeue(); ASSERT_NE(n, nullptr); // 后续要解引用,必须用 ASSERT_ EXPECT_EQ(*n, 1); ... }

关键机制:每个TEST_F测试运行时,googletest 都会新建一个全新的夹具对象,依次执行构造 →SetUp()→ 测试体 →TearDown()→ 析构,且不同测试之间绝不复用同一夹具对象,一个测试对夹具的改动不会影响其他测试。此例也示范了ASSERT_*的正确使用场景:ASSERT_NE(n, nullptr)之后要解引用n,若为 NULL 将段错误。

运行测试与 main() 函数

TEST()/TEST_F()会自动向框架注册测试,无需手动罗列。调用RUN_ALL_TESTS()即可运行链接单元内的全部测试(可跨文件、跨测试用例),全部成功返回 0,否则返回 1。流程为:保存 gtest flags 状态 → 对每个测试构造夹具、SetUp()、运行、TearDown()、析构 → 恢复 flags 状态。

两条强制规则:

  1. 绝不能忽略RUN_ALL_TESTS()的返回值(否则编译报错)——自动化测试服务依据的是进程退出码,因此main()必须return RUN_ALL_TESTS();;
  2. 只能调用一次——多次调用会与死亡测试等高级特性冲突,不受支持。

编写自定义main()的标准骨架:

#include "gtest/gtest.h" int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }

::testing::InitGoogleTest()负责解析命令行中的 gtest flags 并从参数中移除它们,必须在RUN_ALL_TESTS()之前调用(Windows 的 UNICODE 模式下亦支持宽字符串)。如果不想写main(),直接链接gtest_main库即可——Robomongo 的robo_unit_tests正是同时链接gtest与gtest_main的做法。

常用配置宏:按需微调 googletest

googletest/README.md 指出,框架默认配置在特殊环境下可能不适用,可用编译命令行定义GTEST_XYZ类控制宏(赋 1 或 0)来开关特性。完整清单见 include/gtest/internal/gtest-port.h,最常用的有:

宏作用
-DGTEST_USE_OWN_TR1_TUPLE=0项目已用 TR1 tuple 时,让 gtest 使用同一实现,避免两套 tuple 冲突
-DGTEST_USE_OWN_TR1_TUPLE=1强制 gtest 使用自带的 TR1 tuple 子集实现
-DGTEST_HAS_TR1_TUPLE=0完全禁用 tuple 及依赖 tuple 的特性
-DGTEST_HAS_PTHREAD=1/=0pthread 检测失败时手动指定是否可用;#include "gtest/gtest.h"后可查GTEST_IS_THREADSAFE宏确认线程安全状态
-DGTEST_CREATE_SHARED_LIBRARY=1把 gtest 编译为共享库(Windows 上即 DLL),需配合链接器选项
-DGTEST_LINKED_AS_SHARED_LIBRARY=1编译使用共享 gtest 的测试代码时必须加
-DGTEST_DONT_DEFINE_FOO=1宏名冲突时改名:FOO可为FAIL、SUCCEED、TEST,加此宏后需写GTEST_TEST(...)而非TEST(...)

其中"共享库"两条宏,文档建议即便当前编译器(如 GCC)不加也能工作,也应始终加上,以兼容未来的库加载优化(visibility 相关)。

已知限制

Primer 在结尾列出的限制同样值得测试作者注意:googletest 设计为线程安全,但实现仅在 pthread 可用的系统上才是线程安全的;在其他系统(例如 Windows)上,两线程并发调用断言目前并不安全。绝大多数测试都在主线程完成断言,通常不受影响。

延伸阅读

  • 入门概念:googletest/docs/primer.md;进阶主题(死亡测试、参数化测试、flag 详解等):googletest/docs/advanced.md;FAQ:googletest/docs/faq.md;
  • Mock 扩展:googlemock/README.md 与 googlemock/docs/CookBook.md;
  • 仓库内构建指南:docs/BuildRobo3TOnMacAndLinux.md、docs/BuildRobo3TOnWindows.md、docs/BuildRobo3TShell.md;
  • 测试工程配置:src/robomongo-unit-tests/CMakeLists.txt 与 src/robomongo-unit-tests/README.md。

如果希望为新模块补测,遵循仓库约定即可:把Foo_test.cpp与Foo.cpp/Foo.h放在一起,按TEST(用例名, 场景_行为)风格书写断言,再把文件登记进 src/robomongo-unit-tests/CMakeLists.txt 的SOURCES_TEST,即可获得与既有测试一致的构建与运行体验。

  • 数据库客户端
  • 桌面应用

【免费下载链接】robomongo

Native cross-platform MongoDB management tool

项目地址:https://gitcode.com/gh_mirrors/ro/robomongo
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 10:16:41

313MB加密包分析:揪出静默上传暗门

最近接手了一个样本分析任务&#xff0c;拿到手上的样本是一个313MB的加密压缩包。单从文件名来看平平无奇&#xff0c;后缀是rar&#xff0c;备注写着“产品资料备份v3.2”&#xff0c;但当同事告诉我这个包是从一台中了招的内部服务器上导出来的&#xff0c;而且文件体积异常…

作者头像 李华
网站建设 2026/9/25 10:14:26

圆柱壳自由振动分析:切比雪夫多项式与Sanders理论实战

简介&#xff1a;本资源是一份面向结构动力学研究者与工程技术人员的圆柱壳自由振动分析技术资料&#xff0c;聚焦Sanders壳体理论在任意边界条件下的建模与求解&#xff0c;解决传统方法难以统一处理复杂边界&#xff08;如弹性约束、混合支撑&#xff09;的痛点。包内含1个91…

作者头像 李华
网站建设 2026/9/25 10:14:03

本地化AI代码审查工具:CLI+Git钩子+本地LLM实战指南

1. 项目概述&#xff1a;这不是又一个“AI写代码”玩具&#xff0c;而是一套可嵌入真实开发流水线的开源代码审查协作者open-code-review 这个名字乍看平平无奇&#xff0c;但拆开来看——“open”不是指“开源”&#xff0c;而是指“开放上下文、开放意图、开放协作过程”&…

作者头像 李华