mcpField
M
mutation-testing

mutation-testing

github.com/jwilger测试与质量5101 次安装17 周前更新
代码仓库

关于 mutation-testing

mutation-testing 是一项指导 Agent 执行变异测试的技能,适用于将测试质量检查纳入 PR 前流程的开发团队。它依据项目类型选择工具,并通过变异存活情况检查测试覆盖的行为是否充分。

主要功能

  1. 01按项目标记选择变异测试工具
  2. 02将测试范围限定在相关代码
  3. 03汇总变异数量、存活数与得分
  4. 04按场景检查存活变异
  5. 05未达到 100% 时阻止默认 PR 流程

适用场景

  1. 01在创建 PR 前验证测试能否捕获缺陷
  2. 02定位未被测试发现的代码变更
  3. 03为存活变异关联场景并提出测试建议

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jwilger/agent-skills --skill mutation-testing

安装后在 Claude Code 中输入/mutation-testing确认已加载。

详细介绍

突变测试

**价值:**反馈——突变测试通过证明测试确实能检测到它们声称可以防止的错误,从而闭合验证环。没有突变测试,通过的测试可能会带来虚假的信心。

目的

教会 Agent 在创建 PR 前,将突变测试作为质量门禁。突变测试会对生产代码做小幅修改(突变),并检查测试能否捕获这些修改。未被检测到的突变体揭示了可能让错误逃过检测的测试缺口。要求突变杀死率达到 100%。

实践

检测并运行合适的工具

检测项目类型并运行相应的突变测试工具。

  1. 检查项目标记:

    • Cargo.toml -> Rust -> cargo mutants
    • package.json -> TypeScript/JavaScript -> npx stryker run
    • pyproject.toml 或 setup.py -> Python -> mutmut run
    • mix.exs -> Elixir -> mix muzak
  2. 确认工具已安装。如果没有,请提供安装说明:

    • Rust:cargo install cargo-mutants
    • TypeScript:npm install --save-dev @stryker-mutator/core
    • Python:pip install mutmut
    • Elixir:在 deps 中添加 {:muzak, "~> 1.0", only: :test}
  3. 针对相关范围运行突变测试。尽可能优先限定到已更改的文件或包,而不是整个代码库:

    # Rust (scoped to package)
    cargo mutants --package <package> --jobs 4
    
    # TypeScript
    npx stryker run
    
    # Python (scoped to source)
    mutmut run --paths-to-mutate=src/
    mutmut results
    
    # Elixir
    mix muzak
    
解析并报告结果

从突变测试工具的输出中提取:

  • 生成的突变体总数
  • 被杀死的突变体(测试检测到了更改)
  • 存活的突变体(测试未检测到更改)
  • 超时的突变体
  • 突变分数百分比
分析存活的突变体

对于每个存活的突变体,报告以下三项:

  1. **位置:**文件和行号
  2. **突变:**更改内容(例如,“将 + 替换为 -”)
  3. **含义:**这会放过哪类错误

常见突变类型及其存活所表明的问题:

  • 算术(+ -> -、* -> /):计算未经过验证
  • 比较(> -> >=、== -> !=):边界条件未经测试
  • 布尔(&& -> ||、移除 !):逻辑分支未覆盖
  • 返回值(true -> false、Ok -> Err):返回路径未经检查
  • 删除语句(删除某行):副作用未断言
场景覆盖检查

在为存活的突变体推荐任何测试之前,先检查场景覆盖情况:

对于每个存活的突变体:

**步骤 1 — 场景检查:**是否有任何验收场景或领域场景(来自该切片的 acceptance_scenarios 或 domain_scenarios 数组,或计划中的 Confirmed Scenarios 部分)要求被突变的行为?

  • 是 → 存在场景,但缺少相应测试。继续执行下文的“推荐缺失的测试”。
  • 否 → 标记为待人工决策:
    Surviving mutant at [file]:[line] has no GWT scenario requiring this behavior.
    Options:
      (a) Delete the code — this behavior may not be needed.
      (b) Add a missing acceptance or domain scenario — the spec is incomplete.
    The 100% kill rate still applies; this clarifies how to resolve it.
    
    请勿继续为未覆盖的突变体推荐测试。没有场景就编写测试,只是在玩弄指标,并未测试实际行为。
推荐缺失的测试

对于每个有覆盖场景的存活突变体,建议一个具体测试:

Surviving: src/money.rs:45 -- replaced `+` with `-` in Money::add()
Recommend: Test that adding Money(50) + Money(30) equals Money(80),
           not Money(20). The current tests do not assert the sum value.

Surviving: src/account.rs:78 -- replaced `>` with `>=` in check_balance()
Recommend: Test the exact boundary -- check_balance with exactly zero
           balance. Current tests only check positive and negative.
结构化输出

突变测试完成后,生成一个 MUTATION_RESULT 证据包:

{
  "tool": "cargo-mutants",
  "scope": ["src/money.rs", "src/account.rs"],
  "total_mutants": 42,
  "killed": 40,
  "survived": 2,
  "score": 95.2,
  "survivors": [
    {"file": "src/money.rs", "line": 45, "mutation_type": "arithmetic", "description": "replaced + with -"}
  ],
  "verdict": "FAIL"
}
  • **判定:**如果已更改文件的分数为 100%,则为 PASS;否则为 FAIL
  • 在流水线模式下,保存到 .factory/audit-trail/slices/<slice-id>/mutation.json
  • 独立运行时,输出仅供参考——显示后继续执行质量门禁
强制执行质量门禁

要求的变异杀死率为 100%。必须杀死所有变异体。

  • 如果得分为 100%:报告成功,继续创建 PR
  • 如果得分低于 100%:列出所有存活变异体并给出建议。阻止 创建 PR,并明确发出警告。用户可以覆盖此限制,但默认 应先修复。

应做:

  • 尽可能将变异测试范围限定为已更改的代码
  • 报告存活变异体,并提供可执行的修复建议
  • 修复后重新运行,以确认所有变异体均已被杀死
  • 将超时视为已杀死(变异导致了故障)

不要:

  • 在创建 PR 前跳过变异测试
  • 在未报告的情况下接受存活变异体
  • 仅更改了一个模块时,却对整个代码库运行变异测试
  • 建议为应由领域类型负责的数据验证编写测试
流水线模式

由流水线编排器调用时:

  • FAIL 判定会自动连同存活变异体列表一起路由回 tdd 技能。流水线负责处理返工路由——mutation-testing 只负责报告结果。
  • MUTATION_RESULT 数据包中的存活变异体详情必须具体到足以让 TDD 协作双方(文件、行号、变异类型、描述)编写有针对性的测试,而无需重新运行变异测试工具来了解失败原因。
  • 对于同一切片,流水线可能会多次调用 mutation-testing;每次运行都会覆盖该切片之前的 mutation.json。

强制执行说明

  • 流水线模式:门禁。100% 杀死率是门槛——未达标将阻止合并。
  • 独立模式:建议。Agent 会报告结果,但无法阻止用户覆盖限制。

硬性约束:

  • 100% 杀死率:流水线模式下为 [H],独立模式下为 [RP](阻止 PR,用户可附上有记录的理由后覆盖)

覆盖限制的文档要求请参阅模板目录中的 CONSTRAINT-RESOLUTION.md。

约束

  • 100% 杀死率:100% 就是 100%。不是“差不多”,也不是“有理由的 98%”。低于 100% 的唯一途径是用户明确覆盖,此操作必须附上有记录的理由,解释每个存活变异体为何可以接受。“我想发布”不是理由。“这个变异体测试的是日志输出,而不是业务规则”才是理由。
  • “没有场景就不写测试”:仅为杀死变异体而编写没有对应场景的测试是在钻指标的空子。该测试只能证明你能杀死变异体,不能证明这种行为很重要。如果没有场景涵盖该行为,正确做法是将这一缺口报告给团队——缺少的场景可能揭示了需求缺失。

验证

完成变异测试后,请验证:

  • 已针对相关范围运行变异测试工具
  • 已列出所有存活变异体的文件、行号和变异类型
  • 在提出测试建议前,已逐一检查每个存活变异体是否有场景覆盖
  • 对于有场景覆盖的每个存活变异体,都有具体的测试建议
  • 对于没有场景覆盖的每个存活变异体,都已标记以供人工决策
  • 变异得分为 100%(或用户已明确选择覆盖)
  • 如果进行了修复,已重新运行变异测试以确认结果

如果有任何一项未满足,请在继续之前重新执行相关实践。

依赖项

此技能可独立使用,但最适合作为 PR 前的质量门禁。 它与以下内容集成:

  • tdd:TDD 产出由变异测试验证的测试;存活变异体表明 TDD 周期遗漏了某种情况
  • code-review:变异测试结果可为代码审查提供参考——审查者可以检查新增代码是否存在存活变异体

缺少依赖项?使用以下命令安装:

npx skills add jwilger/agent-skills --skill tdd

评论

支持 Markdown