ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MongoDB 单元测试框架实战指南:基于 GoogleTest 的 C++ 测试体系与 Enhanced Reporter

MongoDB 单元测试框架实战指南:基于 GoogleTest 的 C++ 测试体系与 Enhanced Reporter MongoDB 单元测试框架实战指南基于 GoogleTest 的 C 测试体系与 Enhanced Reporter【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo本文以 MongoDB 仓库中 docs/unit_test.md 为骨架系统讲解 MongoDB 基于 GoogleTest 构建的 C 单元测试框架如何通过mongo/unittest/unittest.h头文件与mongo_cc_unit_testBazel 规则编写测试、使用抛异常式断言Throwing Assertions规避 GoogleTest 致命断言的限制、借助 Enhanced Reporter 获得更好的测试输出以及用DEATH_TEST系列宏测试进程终止场景。读完本文你将掌握在 MongoDB 代码库中编写、构建、运行、过滤与调试单元测试的完整方法论并了解框架底层源码的实现原理。框架定位GoogleTest 之上的一层薄封装MongoDB 的单元测试框架并没有另起炉灶而是建立在 GoogleTest 之上的薄封装层。这意味着 GoogleTest 绝大部分能力断言、fixture、参数化测试、mock 等在 MongoDB 单元测试中开箱即用唯一例外是文档明确列出的「禁用特性」Banned Features。框架的入口非常统一只需包含伞状头文件mongo/unittest/unittest.h并通过mongo_cc_unit_testBazel 规则构建测试目标即可。#include mongo/unittest/unittest.h TEST(MySuite, MyTest) { ASSERT_TRUE(true); }从 src/mongo/unittest/unittest.h 的源码可以看到这个伞状头文件以IWYU pragma: export的方式对外导出四个子模块这也是「包含一个头文件即可使用全部能力」的底层原因mongo/unittest/assert.h全套 MongoDB 风格断言宏mongo/unittest/framework.h测试基类、fixture 适配、旧式测试套件兼容等核心框架mongo/unittest/log_capture.h日志捕获工具mongo/unittest/matcher.h为BSONObj等 MongoDB 类型提供的 GoogleMock 匹配器。Bazel 构建规则mongo_cc_unit_test在 bazel/mongo_src_rules.bzl 中定义了mongo_cc_unit_test规则它是 MongoDB 单元测试的标准构建入口。其关键行为包括自动为测试目标追加//src/mongo/unittest:unittest_main依赖除非显式设置provides_main True即测试自带main函数从而自动接入框架的main入口为测试目标自动打上mongo_unittest标签便于在 Bazel 查询与 CI 流水线中统一筛选底层委托给mongo_cc_test完整透传srcs、deps、data、tags、copts、linkopts、features、exec_properties等参数。一个最小示例参照 src/mongo/unittest/BUILD.bazel 中框架自测目标unittest_test的写法mongo_cc_unit_test( name my_test, srcs [my_test.cpp], deps [ //src/mongo/db:server_base, ], )框架自身的测试目标unittest_test位于 src/mongo/unittest/BUILD.bazel它同时依赖golden_test_test.cpp、temp_dir_test.cpp、log_capture_test.cpp等多个自测文件是学习框架各组件用法的第一手样例集。GoogleTest 特性参数化测试与 GoogleMock参数化测试Value-Parameterized / Typed Tests参数化测试是 GoogleTest 的核心特性之一允许同一份测试逻辑以不同值或不同类型反复运行。文档给出的典型场景是测试一个读取操作在不同读关注级别Read Concern Level下的行为。class TestFixture : public testing::TestWithParammongo::repl::ReadConcernLevel { }; INSTANTIATE_TEST_SUITE_P(TestSuite, TestFixture, testing::Values(ReadConcernLevel::kLocalReadConcern, ReadConcernLevel::kMajorityReadConcern)); TEST_P(TestFixture, MongoTest) { ... sendRead(GetParam()); // 依次使用 kLocalReadConcern 或 kMajorityReadConcern 运行 }要点说明TestFixture继承testing::TestWithParamTGetParam()在测试体内取回当前参数INSTANTIATE_TEST_SUITE_P通过testing::Values(...)枚举参数集合每个参数都会生成一个独立的测试实例除 Value-Parameterized 外GoogleTest 还提供 Typed Tests类型参数化在 MongoDB 框架中同样可用。GoogleMockGoogleMock 随框架一并提供只需包含mongo/unittest/unittest.h即可使用。文档明确要求不要直接包含gmock/gmock.h原因在于 src/mongo/unittest/framework.h 中gmock/gmock.h与gtest/gtest.h均由框架统一导出直接包含可能破坏框架对 GoogleTest 内部宏的定制见下文「Throwing Assertions」一节。对 MongoDB 特有类型框架在 src/mongo/unittest/matcher.h 中提供了现成的匹配器例如针对BSONObj的匹配器配合ASSERT_THAT即可写出语义清晰的断言。另外框架还在 src/mongo/unittest/framework.h 中暴露了MockBehavior枚举nice/naggy/strict。GoogleMock 默认行为是 naggy对未设置期望的调用发出警告而 MongoDB 框架将其包装后的默认值调整为 nice静默接受可通过getDefaultMockBehavior()/setDefaultMockBehavior()在单个测试内查询与调整且该调整会在每个测试用例结束后自动重置。Throwing Assertions用异常代替 return 的致命断言这是 MongoDB 单元测试框架与原生 GoogleTest 最关键的差异点也是理解整套断言宏设计的前提。GoogleTest 的致命断言fatal assertion失败时通过return;提前退出当前函数这带来两个限制无法在非 void 返回值的辅助函数中使用致命断言因为无法确定返回什么断言后无法执行析构与清理逻辑。MongoDB 框架改为致命断言触发时抛出一个异常TestAssertionFailureException即testing::AssertionException定义见 src/mongo/unittest/framework.h从而规避上述限制。其底层实现位于 src/mongo/unittest/framework.h框架#undef了GTEST_FATAL_FAILURE_将其从「展开为return」重定义为不带 return 的版本#undef GTEST_FATAL_FAILURE_ #define GTEST_FATAL_FAILURE_RETURN_ #define GTEST_FATAL_FAILURE_(message) \ GTEST_FATAL_FAILURE_RETURN_ GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure)这意味着 GoogleTest 的所有致命断言宏ASSERT_EQ、ASSERT_TRUE等在 MongoDB 框架中失败时都会走异常路径。这也解释了为什么编写测试时不应直接包含gmock/gmock.h——绕过框架头文件会丢失这一定制。增强断言宏ASSERT 家族在 src/mongo/unittest/assert.h 中框架以 GoogleTest 宏为基础包装并扩充了一整套 MongoDB 风格的断言宏基础断言FAIL(msg)无条件失败并输出消息ASSERT(EXPRESSION)等价于ASSERT_TRUE(EXPRESSION)ASSERT_OK(EXPRESSION)/ASSERT_NOT_OK(EXPRESSION)断言Status是否为 OK非 OK源码实现为ASSERT_EQUALS(::mongo::Status::OK(), (EXPRESSION))ASSERT_EQUALS/ASSERT_NOT_EQUALS/ASSERT_LESS_THAN/ASSERT_GREATER_THAN/ASSERT_LESS_THAN_OR_EQUALS/ASSERT_GREATER_THAN_OR_EQUALS等全套比较断言分别映射到 GoogleTest 的ASSERT_EQ/NE/LT/GT/LE/GEASSERT_APPROX_EQUAL(a, b, absoluteErr)近似相等映射ASSERT_NEAR适合低精度浮点比较。异常断言ASSERT_THROWS(expression, exceptionType)断言表达式抛出指定类型或其子类型异常ASSERT_THROWS_WHAT(expression, exceptionType, expectedWhat)额外校验what()返回值ASSERT_THROWS_CODE(expression, exceptionType, expectedCode)额外校验异常getCode()返回的错误码ASSERT_THROWS_CODE_AND_WHAT(...)同时校验错误码与消息ASSERT_THROWS_WITH_CHECK(EXPRESSION, EXCEPTION_TYPE, CHECK)捕获异常后继续执行一段CHECK(ex)断言块ASSERT_DOES_NOT_THROW(expression)断言不抛异常。BSON 断言针对 MongoDB 最常用的BSONObj/BSONElement类型框架提供了一组专门的比较宏ASSERT_BSONOBJ_EQ / LT / LTE / GT / GTE / NE按默认比较器比较两个BSONObjASSERT_BSONOBJ_EQ_UNORDERED(...)等无序变体忽略字段顺序适合比较文档内容;ASSERT_BSONOBJ_BINARY_EQ(a, b)要求线上二进制完全一致binaryEqual而非逻辑相等ASSERT_BSONELT_EQ等针对BSONElement的同类比较宏。其他实用断言ASSERT_STRING_CONTAINS/ASSERT_STRING_OMITS基于HasSubstr的子串断言ASSERT_STRING_SEARCH_REGEX正则匹配断言ASSERT_DOES_NOT_COMPILE(Id, Alias, ...)编译期断言——验证某个表达式在给定模板别名下无法编译利用 SFINAE仅限表达式需放在命名空间作用域而非TEST函数体内ASSERT_BSONOBJ_EQ_AUTO/ASSERT_STR_EQ_AUTO/ASSERT_NUMBER_EQ_AUTO自动更新断言失败时可将期望值回写进源文件配合--autoUpdateAsserts命令行选项使用。此外工具函数assertGet(const StatusWithT)见 src/mongo/unittest/assert.h用于从StatusWithT中安全取出值状态非 OK 时直接抛异常。Enhanced Reporter增强测试输出框架默认启用的EnhancedReporter实现见 src/mongo/unittest/enhanced_reporter.h 与enhanced_reporter.cpp改进了测试报告的呈现方式彩色化与格式化输出在写入 TTY 且未显式禁用颜色的情况下对关键信息着色进度指示器向 TTY 输出时维护一个已运行测试数量的进度指示增强的失败信息对失败测试打印带文件与行号的详细信息抑制通过测试的日志输出默认只展示失败测试的细节避免刷屏。从 src/mongo/unittest/enhanced_reporter.h 的Options结构可以看到其行为细节useColor取决于「stdout 是否为 TTY」以及 GoogleTest 的颜色配置gtestColorEnabled()或gtestColorDefaulted()useSpinner跟随useColorANSI 转义序列仅在输出端可接受时使用。与之相关的命令行选项实际解析代码位于 src/mongo/unittest/unittest_options.cpp--showEachTest关闭对日志输出的抑制逐个展示所有测试结果默认仅展示失败的--enhancedReportertrue/false启用或禁用整个 Enhanced Reporter 行为默认值为true。命令行选项详解MongoDB 单元测试框架在 GoogleTest 自带参数之外提供了一套自定义命令行选项解析逻辑集中在 src/mongo/unittest/unittest_options.cpp最终映射为 src/mongo/unittest/unittest_options.h 中的UnitTestOptions结构体。全部选项如下选项类型说明--help开关列出所有支持的单元测试选项--list开关列出本测试二进制内的所有测试套件--suitestring可重复指定要运行的测试套件名可多次指定以运行多个套件--filterstring字符串以正则部分匹配测试用例名进行过滤--fileNameFilterstring字符串按源文件名正则部分匹配过滤测试用例--repeatint整数指定每个测试的运行次数默认1--verbose字符串输出更详细的日志可叠加多个v如-vv提高详细程度默认隐式值为v--tempPathstring字符串放置mongo::TempDir子目录的根目录--autoUpdateAsserts开关对支持自动更新的断言自动回写期望输出--rewriteAllAutoAsserts开关为自测目的重写所有自动更新断言--enhancedReporterbool布尔是否使用增强测试报告器默认true--showEachTest开关配合--enhancedReporter使用展示所有测试结果而不只展示失败的从 src/mongo/unittest/unittest_options.cpp 的解析器实现可以看到框架使用--作为选项终止符支持--optvalue与--opt value两种传参形式未识别的参数会原样保留并透传给 GoogleTest 自身解析。测试二进制入口main位于 src/mongo/unittest/unittest_main.cpp它会先initialize()完成框架初始化再进入测试执行。典型用法示例# 运行单个套件 ./bazel-bin/src/mongo/db/query/query_test --suiteQueryPlannerTest # 用正则过滤用例并重复运行 3 次 ./my_test --filter*Insert* --repeat3 # 查看该测试二进制支持的全部自定义选项 ./my_test --helpDeath Tests测试进程终止场景部分代码路径如fassert失败、致命信号会导致进程终止普通测试无法覆盖这类场景需要使用死亡测试。MongoDB 框架用DEATH_TEST系列宏取代了 GoogleTest 的ASSERT_DEATH提供四个变体定义见 src/mongo/unittest/death_test.hDEATH_TEST(SUITE_NAME, TEST_NAME, MATCH_EXPR)字符串子串匹配DEATH_TEST_REGEX(SUITE_NAME, TEST_NAME, REGEX_EXPR)使用 PCRE 正则做部分匹配DEATH_TEST_F(FIXTURE_NAME, TEST_NAME, MATCH_EXPR)结合 fixture 的版本DEATH_TEST_REGEX_F(FIXTURE_NAME, TEST_NAME, REGEX_EXPR)fixture 正则版本。按 GoogleTest 的约定死亡测试的套件名应以DeathTest结尾便于框架在运行死亡测试前重新启动进程。例如某个普通测试属于SuiteName则其死亡测试应命名为SuiteNameDeathTestTEST(SuiteName, TestName) { ... } DEATH_TEST(SuiteNameDeathTest, TestName) { ... } using FixtureNameDeathTest FixtureName; DEATH_TEST_F(FixtureNameDeathTest, TestName) { ... }从 src/mongo/unittest/death_test.h 的宏展开实现可以看到底层机制每个死亡测试会fork一个子进程执行SetUp() → TestBody() → TearDown()并调用TestingProctor::instance().exitAbruptlyIfDeferredErrors()确保延迟错误也会触发异常退出测试仅在子进程异常终止如fassert失败或致命信号时才算成功DeathTestT通过DeathTestStyleFlagGuard将 GoogleTest 的death_test_style标志临时设置为threadsafe执行完再恢复src/mongo/unittest/death_test.h死亡测试与「已运行中的线程」不兼容如果测试需要多线程应在测试体内启动线程或使用DEATH_TEST_F并在setUp()中启动。使用注意由于ASSERT_DEATH属于框架明确列出的禁用特性编写死亡测试时务必改用DEATH_TEST系列宏。禁用特性与未来规划Banned Features禁用特性ASSERT_DEATH不应使用。应改用DEATH_TEST详见上文 Death Tests 一节。Upcoming Features规划中特性文档同时列出了框架的演进方向均为未落地的规划彩色输出增加 Evergreen 支持并在未检测到终端时过滤颜色输出过滤 / 格式化面向单元测试开发体验的新 API。这些内容目前尚未在源码中实现属于路线图性质读者可在 docs/unit_test.md 中持续跟踪。VSCode 集成发现、运行与调试测试仓库的 Linux 默认工作区文件.vscode_defaults/linux-virtual-workstation.code-workspace内置了对单元测试的发现、运行与调试支持依赖 TestMate C 扩展。前置条件必须直接以工作区文件方式打开 VS CodeFile → Open Workspace from File选择.vscode_defaults/linux-virtual-workstation.code-workspace安装 TestMate C 扩展扩展 IDmatepek.vscode-catch2-test-adapter已列入该工作区的推荐扩展列表。发现测试TestMate 会监视bazel-bin/src/mongo/**/*test*_with_debug路径下的测试二进制测试在二进制构建完成之前不会被发现。因此先构建目标bazel build --configdbg bson_test # 产物位于 bazel-bin/src/mongo/bson/bson_test构建完成后Test Explorer 面板会按「二进制 → 测试套件 → 测试名」三级结构展示所有发现的测试。调试单个测试在源文件中要暂停的代码行左侧单击设置断点出现红色圆点在 Test Explorer 中点击对应测试旁边的Debug按钮此时Run and Debug面板打开可查看Call Stack调用栈、Variables变量与Watch监视表达式。小结MongoDB 单元测试框架通过「GoogleTest 薄封装 抛异常断言 增强报告器 专属 Death Test」的组合为这个超大规模 C 代码库提供了统一、可扩展、开发者友好的测试底座。对测试作者而言核心实践可以浓缩为三点统一包含mongo/unittest/unittest.h、用mongo_cc_unit_test声明测试目标、借助--filter/--suite/--showEachTest等选项精细控制运行行为对框架使用者而言src/mongo/unittest/BUILD.bazel 中的unittest_test自测目标与 src/mongo/unittest/unittest_test.cpp 提供了最直接的用法范例值得作为学习入口。【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表