ARTICLE DETAIL

资讯详情

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

MongoDB Golden Data 测试框架完整实战指南

MongoDB Golden Data 测试框架完整实战指南 MongoDB Golden Data 测试框架完整实战指南【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongoGolden Data黄金数据测试是 MongoDB 开源仓库中一套基于录制-对比思想的测试方案测试运行产生文本输出框架将其与仓库中签入的、已知正确的期望输出即 golden data逐字节比对任何差异都会导致测试失败迫使开发者要么修改代码、要么批量接受新输出。本文以 docs/golden_data_test_framework.md 为核心脉络结合 golden_test.h、golden_test_base.cpp 与 buildscripts/golden_test.py 等源码实现完整讲解其适用场景、编写方式、工作台 diff/accept 工作流与配置参考读完即可在 MongoDB 源码仓库中落地一套可批量 diff、批量接受的 Golden Data 测试。一、Golden Data 测试是什么Golden Data 测试框架用于运行并管理这样一类测试测试产生输出而输出通过与仓库中已签入的已知正确输出对比来验证。任何差异都会导致测试失败此时需要更新代码或期望输出。该框架在批量 diff 失败测试输出、批量接受新测试输出方面尤其高效。从源码层面看其核心机制位于 golden_test_base.cpp 的verifyOutput()测试结束时框架把GoldenTestContext累积的输出字符串与goldenDataPath指向的期望文件做逐字节比较不一致则调用failResultMismatch()触发失败。若期望文件根本不存在同样判定失败golden_test_base.cpp。何时应该使用 Golden Data 测试根据文档满足以下条件时适合引入 Golden Data 测试被测代码输出是确定性的只有输出确定测试才能稳定地成功或失败代码或测试夹具的增量变更会带来输出的增量变更便于通过 diff 精准定位变更作为大型输出比较场景下 ASSERT 的替代方案用途一致但额外提供了 diff 与更新工具输出无法用客观属性验证例如无法验证已知属性时。典型例子验证排序是否生效可以直接断言输出已排序不应使用 Golden Data 测试验证pretty printing 是否正确时输出可能没有可客观检查的属性、或属性容易变化可以使用 Golden Data 测试作为稳定性/版本化/回归测试通过存储录制输出Golden Data 测试非常适合在变更发生时保护历史版本行为、检测非预期行为变化——即使新行为满足其他正确性标准。从仓库的实际应用看jstests/query_golden/expected_output/目录正是查询子系统 Golden Data 期望输出的典型落点其 BUILD 文件定义了expected_output目标说明该机制在查询测试套件中已被大规模使用。与 ASSERT 的取舍Golden Data 测试与断言如ASSERT_EQ定位互补断言适合验证少量、可客观判定的性质当输出体量大、形式自由、缺少可验证的客观属性时Golden Data 测试以整段输出对比 批量 diff/接受工具补足缺口。文档明确指出测试输出必须像ASSERT_EQ一样确定且可重复包括跨平台。二、Golden Data 测试最佳实践输出的基本要求MUST产生可 diff、可在 Pull Request 中审阅的文本输出MUST输出确定、可重复包括在不同平台上运行与ASSERT_EQ相同要求SHOULD输出随测试/代码的增量修改而增量变化避免大段无关 diff。测试变体的组织多个测试变体可以打包进单个测试在同一特性、不同输入的场景下尤其推荐。好处有二相似测试输出集中在一起便于审阅同时减少输出文件数量。文档给出的runVariation辅助函数模式见下文示例正是这一实践的标准写法。夹具Fixture共享策略同一套件内的测试 SHOULD 共享夹具降低新增测试成本夹具变更只影响该套件的期望输出且可批量更新不同套件之间 SHOULD NOT 复用/共享夹具因为夹具变更可能波及大量期望输出。例外情况夹具已稳定、极少变更套件相关共享测试或测试相似组件搭建/拆除成本过高出于性能原因必须共享同一实例。输入输出同时打印测试 SHOULD 同时打印被测代码的输入与输出让审阅者无需跨文件反查就能确认期望输出是否正确——否则寻找产生新输出的输入可能不现实甚至不在 diff 范围内。合并冲突的处理期望输出文件expected_output被视为自动生成文件因此合并冲突应按下述方式处理Accept theirs接受对方版本后重跑测试并验证新输出。无需了解对方分支的生产/测试代码变更但需要重新审阅并接受本分支的改动Accept yours接受本分支版本后重跑测试并验证新输出。需要了解对方分支的代码变更但当输出变更简单而重复如打印代码或夹具变更所致时比重新审视本地改动更容易验证。期望输出在紧耦合套件间的复用期望输出 SHOULD 在紧耦合套件间复用。所谓紧耦合指满足以下任一条件共享相同的测试、输入与夹具测试相似场景测试不同代码路径但一条路径的变更预期伴随另一条路径的变更。套件间存在正当且预期的输出差异时测试应使用不同测试文件。示例在不同环境下测试同一行为的单元测试、集成测试与功能测试版本化测试——大多数输入/场景下期望行为一致。禁止手工修改期望输出AVOID 手工修改期望输出文件——它们被视为自动生成文件。正确做法是运行测试然后把生成的实际输出复制为新的期望输出详见下文如何 diff 与接受新输出。三、如何编写 Golden Data 测试每个 Golden Data 测试应产出后续将被验证的文本输出。输出格式必须是文本具体格式text/json/bson/yaml 或混合由测试作者自定若测试含多个变体各变体必须清晰分隔。注测试输出通常只写不读聚焦实现序列化/打印代码即可无需提供反序列化/解析代码。当实际输出与期望输出不一致时框架会令测试失败、记录双方输出并生成以下可后续检查的文件output_path/actual/test_path—— 实际测试输出output_path/expected/test_path—— 期望测试输出其中output_path由outputRootPattern决定见 附录配置文件参考。C 测试核心类型C Golden Data 测试由两个核心类型支撑::mongo::unittest::GoldenTestConfig配置测试套件定义期望输出文件在源码仓库中的位置。其relativePath字段是相对于仓库根目录的路径多个套件可共享同一路径golden_test_base.h::mongo::unittest::GoldenTestContext提供测试写输出的输出流并用仓库中的期望输出验证实际输出golden_test.h。底层实现中GoldenTestContext在析构时validateOnClose为真且无未捕获异常时自动调用verifyOutput()golden_test_base.h。测试路径由套件名与测试名经sanitizeName转为 snake_case拼接而来例如自测用例断言GoldenSelfTest/GoldenTestContextGetPath生成golden_self_test/golden_test_context_get_path.txtgolden_test_test.cpp。完整示例文档中的标准示例输出文件默认落在src/mongo/my_expected_output#include mongo/unittest/golden_test.h GoldenTestConfig myConfig(src/mongo/my_expected_output); TEST(MySuite, MyTest) { GoldenTestContext ctx(myConfig); ctx.outStream() print something here std::endl; ctx.outStream() print something else std::endl; } void runVariation(GoldenTestContext ctx, const std::string variationName, T input) { ctx.outStream() VARIATION variationName std::endl; ctx.outStream() input: input std::endl; ctx.outStream() output: runCodeUnderTest(input) std::endl; ctx.outStream() std::endl; } TEST_F(MySuiteFixture, MyFeatureATest) { GoldenTestContext ctx(myConfig); runMyVariation(ctx, variation 1, some input testing A #1) runMyVariation(ctx, variation 2, some input testing A #2) runMyVariation(ctx, variation 3, some input testing A #3) } TEST_F(MySuiteFixture, MyFeatureBTest) { GoldenTestContext ctx(myConfig); runMyVariation(ctx, variation 1, some input testing B #1) runMyVariation(ctx, variation 2, some input testing B #2) runMyVariation(ctx, variation 3, some input testing B #3) runMyVariation(ctx, variation 4, some input testing B #4) }要点拆解ctx.outStream()返回std::ostream测试用累积输出golden_test_base.h每个变体用VARIATION name行分隔并同时打印 input 与 output符合最佳实践期望文件路径 goldenDataRoot / relativePath / suite/test.txtgolden_test_base.cpp。失败时的行为失败时GoldenTestContext::onError会以日志 ID6273501记录失败信息与路径属性执行diffCmd默认git diff --no-index {{expected}} {{actual}}并把 diff 输出转发到当前进程最后调用GTEST_FAIL_AT在测试源码位置失败golden_test.cpp。这也解释了非工作站测试结果 diff一节中为何能从日志里 grep 到 ID 6273501 的属性。提示运行bazel test前需先按下文Setup配置好框架确保 C 测试输出写入buildscripts/golden_test.py可发现的位置diff/accept功能才能按预期工作。四、如何编写JS/查询Golden Data 测试文档的 C 部分之外仓库还提供了 JS 层的 Golden Data 测试能力。buildscripts/golden_test.py的clean-run-accept子命令通过resmoke.py find-suites定位 jstest 所属的 passthrough 套件并逐一运行golden_test.py。若测试只属于query_golden_classicpassthrough脚本会推断该测试可能因不同构建变体下不同的internalQueryFrameworkControl设置而产生多份期望结果于是用四种参数组合分别运行--mongodSetParameters{internalQueryFrameworkControl: forceClassicEngine} --excludeWithAnyTagsrequires_sbe --mongodSetParameters{internalQueryFrameworkControl: trySbeEngine} --mongodSetParameters{internalQueryFrameworkControl: trySbeRestricted} --additionalFeatureFlagsfeatureFlagSbeFull --disableFeatureFlagsfeatureFlagGetExecutorDeferredEngineChoice每次 resmoke 运行因 Golden Data 测试失败而返回非零退出码时脚本会调用accept接受新结果golden_test.py。jstests/query_golden/目录下的expected_output/即这类测试期望输出的存放位置。五、在工作台 diff 与接受新测试输出使用buildscripts/golden_test.py命令行工具管理测试输出支持diff 指定测试运行输出中所有测试的输出差异accept接受指定测试运行输出中所有测试的输出差异。Setup一次性配置buildscripts/golden_test.py需要一次性的工作台配置。注意该配置仅为使用golden_test.py所需不运行 Golden Data 测试时无需配置。配置两步创建 YAML 配置文件结构见 附录配置文件参考设置GOLDEN_TEST_CONFIG_PATH环境变量指向该文件使其在运行测试与运行golden_test.py时都可用。自动 SetupLinuxbuildscripts/golden_test.py setupWindowsc:\python\python310\python.exe buildscripts/golden_test.py setup非以管理员身份运行的 shell 可能会被询问密码。从源码看setup_linux()会在~/.golden_test_config.yml写入默认配置并把GOLDEN_TEST_CONFIG_PATH追加到/etc/environmentsetup_windows()则在%LocalAppData%\.golden_test_config.yml写配置并通过runas ... setx设置全局环境变量golden_test.py。手动 Setup默认配置与自动 Setup 相同的配置。该配置为每次测试运行使用唯一子目录默认行为允许分别 diff 每次测试运行兼容多个源码仓库。Linux/macOS创建~/.golden_test_config.ymloutputRootPattern: /var/tmp/test_output/out-%%%%-%%%%-%%%%-%%%% diffCmd: git diff --no-index {{expected}} {{actual}}更新.bashrc/.zshrcexport GOLDEN_TEST_CONFIG_PATH~/.golden_test_config.ymlDebugger/IDE 等场景也可改用/etc/environment或其他配置方式。Windows创建%LocalAppData%\.golden_test_config.ymloutputRootPattern: C:\Users\Administrator\AppData\Local\Temp\test_output\out-%%%%-%%%%-%%%%-%%%% diffCmd: git diff --no-index {{expected}} {{actual}}设置环境变量runas /profile /user:administrator setx GOLDEN_TEST_CONFIG_PATH %LocalAppData%\.golden_test_config.yml日常用法列出所有可用的测试输出$ buildscripts/golden_test.py listdiff 最近一次测试运行的结果运行配置中指定的 diffCmd$ buildscripts/golden_test.py diff接受最近一次测试运行的结果把该次运行的全部实际输出复制到源码仓库成为新的期望输出$ buildscripts/golden_test.py accept实现上accept先通过git rev-parse --show-toplevel获取仓库根目录再把output/actual/下的文件递归复制到仓库根golden_test.py。由于期望文件路径形如relativePath/suite/test.txt复制后即还原为仓库内的期望输出结构。获取最近一次测试运行的路径供自定义工具使用$ buildscripts/golden_test.py get $ buildscripts/golden_test.py get_root查看全部命令与选项$ buildscripts/golden_test.py --help除文档列出的命令外源码还提供clean删除所有测试输出、latest打印最近测试输出名与get-path打印输出根路径等子命令并支持-n/--dry-run与-v/--verbose全局选项golden_test.py。一次更新多个期望文件部分测试会在多个 passthrough 或构建变体中运行因此存在多份期望文件。测试一旦更新所有期望文件应同步更新buildscripts/golden_test.py --verbose clean-run-accept jstests/query_golden/NAME_OF_TEST.js该命令使用resmoke.py find-suites确定测试所属的 passthrough 套件并运行之实现见 golden_test.py。若测试仅属于query_golden_classicpassthrough则假定它可能因不同构建变体下不同的internalQueryFrameworkControl设置而产生多份期望结果会按上文所列参数组合运行。六、如何 diff 非工作站测试运行的结果当测试在 CI 等非工作站环境运行时可通过解析测试日志来 diff 结果。批量文件夹 diff解析测试日志找到期望与实际输出文件写入的根输出位置对比两个文件夹查看失败测试的差异。示例Linux/macOS# 显示本次测试运行的期望与实际输出文件夹 $ cat test.log | grep ^{ | jq -s -c -r .[] | select(.id 6273501 ) | .attr.expectedOutputRoot .attr.actualOutputRoot | sort | uniq # 递归 diff $ diff -ruN --unidirectional-new-file --coloralways expected_root actual_root日志 ID6273501正是 golden_test.cpp 中onError使用的日志组件 ID其属性包含expectedOutputRoot与actualOutputRoot。找出失败测试的输出解析日志为每个失败测试定位其期望与实际输出示例Linux/macOS# 找出所有失败测试的期望与实际输出 $ cat test.log | grep ^{ | jq -s .[] | select(.id 6273501 ) | .attr.testPath,.attr.expectedOutput,.attr.actualOutput每条失败日志同时携带testPath、expectedOutput、actualOutput、actualOutputPath、expectedOutputPath与两个输出根路径属性便于脚本化处理。附录配置文件参考Golden Data 测试配置文件是 YAML 格式包含两个可选字段outputRootPattern: type: String optional: true description: Root path patten that will be used to write expected and actual test outputs for all tests in the test run. If not specified a temporary folder location will be used. Path pattern string may use % characters in the last part of the path. % characters in the last part of the path will be replaced with random lowercase hexadecimal digits. examples: /var/tmp/test_output/out-%%%%-%%%%-%%%%-%%%% /var/tmp/test_output diffCmd: type: String optional: true description: Shell command to diff a single golden test run output. {{expected}} and {{actual}} variables should be used and will be replaced with expected and actual output folder paths respectively. This property is not used to decide whether the test passes or fails; it is only used to display differences once weve decided that a test failed. examples: git diff --no-index {{expected}} {{actual}} diff -ruN --unidirectional-new-file --coloralways {{expected}} {{actual}}参数说明与源码印证outputRootPattern可选字符串未指定时使用临时目录每次测试运行会在该模式下生成唯一子目录路径最后一段可含%字符会被替换为随机小写十六进制字符。源码中fs::unique_path正是用随机十六进制填充%golden_test_base.cpp而buildscripts/golden_test.py通过get_path_name_regex把%转为[0-9a-f]正则来枚举输出目录golden_test.py实际输出写入outputRoot/actual/期望输出写入outputRoot/expected/golden_test_base.cpp。diffCmd可选字符串用于 diff 单次测试运行输出的 shell 命令{{expected}}与{{actual}}会被替换为期望与实际输出路径golden_test_base.cpp该属性不决定测试成败仅用于在判定测试失败后展示差异未配置时默认git diff --no-index {{expected}} {{actual}}。环境变量框架通过GOLDEN_TEST_前缀解析环境变量golden_test_base.cppGOLDEN_TEST_CONFIG_PATH可选指定 YAML 配置文件路径GOLDEN_TEST_OUTPUT_ROOT_PATTERN可选覆盖 YAML 中的outputRootPattern供 resmoke 预解析单个输出根并在一次调用中共享给所有 Golden Data 测试见 resmokelib/run/init.py 的_setup_golden_test。七、自测与验证仓库自带 Golden Data 框架的自测用例 golden_test_test.cpp覆盖了四类行为基本输出对比GoldenSelfTest.SanityTest、GoldenSelfTest2.SanityTest2写入固定文本并触发验证测试路径生成GoldenSelfTest.GoldenTestContextGetPath断言套件名/测试名经 snake_case 转换后生成golden_self_test/golden_test_context_get_path.txt异常时不对比GoldenSelfTest2.DoesNotCompareWhenExceptionThrown测试体抛出异常时析构路径不会错误触发验证失败。这些用例同时印证了上文提到的配置复用、路径规则与异常时跳过验证的细节可作为编写新 Golden Data 测试的参照模板。总结Golden Data 测试框架为 MongoDB 提供了一条确定性文本输出 → 与签入期望比对 → 批量 diff → 批量接受的完整闭环适用性判断确定性输出、增量变更、输出难以客观断言、需要回归保护的场景优先使用编写规范C 侧用GoldenTestConfigGoldenTestContext组织套件与输出流遵循变体分组、输入输出并打、共享夹具、紧耦合套件复用期望等最佳实践工作流一次性配置~/.golden_test_config.yml与GOLDEN_TEST_CONFIG_PATH后list/diff/accept/clean-run-accept即可支撑日常迭代与批量更新底层原理从verifyOutput的字节比对、onError的日志与 diff 输出到outputRootPattern的%随机化与 resmoke 的环境变量透传均有源码可查便于按需扩展。相关文件索引框架文档 · C 头文件 · 底层实现 · CLI 工具 · 配置解析 · 自测用例【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表