关于 mutation-testing
mutation-testing 是用于评估测试能否发现代码缺陷的 Agent 技能,通过注入小型变异并重跑测试来检查。它指导从验证基线、限定模块运行到分析幸存变异和报告结果的完整流程。
主要功能
- 01检查测试基线及稳定性
- 02选择并配置对应技术栈的工具
- 03针对单个模块运行变异测试
- 04解释并按风险排列幸存变异
- 05编写业务结果测试并验证击杀
- 06报告评分局限与待审批事项
适用场景
- 01评估单元测试能否发现逻辑错误
- 02优先检查金额或权限代码的测试缺口
- 03不削弱配置地调查幸存变异
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dmoka/ skills --skill mutation-testing安装后在 Claude Code 中输入/mutation-testing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/gotchas.mdjudge.mdtools.md详细介绍
变异测试
覆盖率告诉你代码是否被执行过。变异测试则告诉你,代码出错时测试能否发现。工具一次植入一个小错误(变异体:>= 变成 >,+ 变成 -,或删除某个条件),重新运行测试套件,并记录结果。测试失败会杀死变异体。测试套件仍然全部通过,就说明错误蒙混过关了——每个存活的变异体,都是测试套件在说谎。
参考文件与本文件位于同一目录。如果你是通过 HTTP 而非磁盘获取本文件,请从
https://raw.githubusercontent.com/dmoka/skills/main/skills/mutation-testing/references/ 获取 tools.md、gotchas.md 和 judge.md。
获取原始文本,而非摘要。 许多 Agent 的获取工具会用摘要模型处理页面。它会改写本文件、删掉编号步骤,还会弄乱你即将复制的配置片段。如果获取到的是散文而非下方原样的 Markdown,请使用 curl -sSL <url> 重新获取,并以此为准。
循环
- 基线。 完整运行测试套件两次。若结果为红,报告 BLOCKED — 变异测试工具会拒绝红色基线。两次运行的判定结果不一致,说明测试不稳定,其后所有变异体判定都是噪声。将该测试隔离(跳过,并添加注释说明原因以及需要由人类决定的事项),除非你能确认实际缺陷,且修复就在测试中 — 绝不要凭空编造规范来“修复”测试。如果只有端到端测试/仅 CI 运行的测试,或测试套件在本地运行需要几分钟以上,也要报告 BLOCKED — 变异测试需要快速的本地单元测试套件,而这个缺口本身就是发现的问题。在单体仓库中,应在拥有目标模块的包内操作。绝不要为了得到一个数字而排除失败的测试。
- 选择工具,根据技术栈决定(见下表)。将其安装在项目本地,并按照 references/tools.md 进行配置 — 绝不要运行工具的交互式初始化向导;请自行编写配置文件。任何全局安装前都要先征求同意。
- 首次运行仅针对一个模块 — 选择用户指定的业务关键文件,或静默错误代价最高的文件(金额计算、权限、数据写入)。如果用户没有指定,就自行选择:README 或包的布局通常会指出业务逻辑所在位置,而静默错误代价最高的逻辑所在文件就是起始目标。说明你选择了哪个文件以及原因。在真实代码库中首次运行整个仓库需要数小时,而且常常会失败;针对一个模块运行只需几分钟,就能证明问题所在。首次运行成功后再扩大范围。如果项目足够小,一个模块就是整个代码库,请说明这一点并继续 — 不要为了满足这一步而刻意缩小范围。
- 运行工具时仅输出到控制台,以免在磁盘上留下报告文件(各工具对应的标志见 references/tools.md),并从控制台读取存活的变异体。
- 用一句话解释每个存活的变异体,让非测试人员也能理解:变异体改了什么,以及它会允许哪一个具体输入产生错误输出。示例:“2000 分的上限被删除了:一笔 €1,000 的订单会收取 €30 费用,而不是 €20 的上限金额。”保留数字;“大额订单收费过高”没有告诉读者需要检查什么。
- 按影响范围排列存活的变异体:优先处理金额和数据丢失路径,其次是边界条件,最后是日志和格式问题。
- 消灭变异体或提出主张。 对每个可消灭的存活变异体,编写一个断言业务结果的测试,并重新运行工具确认变异体已被消灭。对于无法消灭的变异体,绝不要自行将其标记为等价 — 想让门禁变绿的 Agent 最有可能把可消灭的变异体称为不可消灭。请改为提出主张:说明为何没有任何输入能区分变异体和原始代码,无论是单独来看还是仅在上下文中如此(references/gotchas.md)。
- 在各自全新的 Agent 中审查每项主张,每项主张对应一名审查者。使用 Claude Code 子 Agent(Agent 工具)、其他工具框架的子 Agent 或委托工具,否则就开启新会话;绝不要在自己的上下文中审查。逐字传入 references/judge.md,并且只附上原始代码行及其函数、变异体、主张和测试命令。立即启动所有审查者,并等待所有判定结果。如果审查者找到能消灭变异体的输入:驳回该主张;保留该测试并重新运行工具确认变异体已被消灭。如果在简述规定的范围内未找到:主张成立,标记为“已审查:未找到可区分输入”。
- 排除前只征求一次同意。 列出每项仍成立的主张及其审查备注,并一次性询问用户是否批准排除。只有得到肯定答复后,才能为每一行添加工具要求的排除注释并说明原因(语法见 references/tools.md)。未经肯定答复,绝不要写排除注释。如果无人可询问(非交互式运行、CI):不写排除注释,将仍成立的主张报告为待决事项,并让门禁保持红色。
- 报告时结果优先,最多 200 个词:
- 一行:工具名称和分数,后接“N 个已消灭,M 个需要你批准”。
- 一个表格,每个存活的变异体占一行:变异体(
file:line,原始代码 → 变异体)· 变化内容,即第 5 步中的具体输入和错误输出 · 已消灭(新测试的名称)或需要你批准(用几个词概括审查者的判定)。每个单元格用一个简短分句。 - 第 9 步中的审批问题,用一两句话表达;或者在无人可答时写“待决事项”。
- 一句话说明分数无法检测到什么(见下文)。再用一句话说明新增或修改的文件,以及磁盘上遗留的任何工具输出。 不包含其他内容:不写设置过程,也不写基线情况,除非运行被 阻塞或不稳定;不列出评审者尝试过的输入。
按技术栈划分的工具
| 技术栈 | 工具 |
|---|---|
| JavaScript / TypeScript | StrykerJS |
| C# / .NET | Stryker.NET |
| Java / Kotlin | Pitest |
| Python | mutmut |
| Rust | cargo-mutants |
| PHP | Infection |
| Go、Ruby 等 | 参见 references/tools.md |
每种工具的设置、配置和限定运行命令:参见 references/tools.md。
严格规则
- 绝不要为了提高分数而更改配置。 分数只是传递信息的信使。 排除文件、延长超时、禁用变异算子或跳过测试文件来让测试通过,是 唯一绝对禁止的做法。
- 杀死变异体的测试必须断言业务结果,而不是变异体行为。 如果你 无法用一句话说明测试为用户保障了什么,那你写这个测试只是为了改变 一个数字——删掉它。
- 关键路径上只要有一个存活的变异体,就比任何分数都重要。 支付 代码中有存活变异体,即使分数达到 95%,结果仍然不合格。不要追求 100%:等价变异体会让这个目标无法实现,而强求达到 100% 会让迭代 无法收敛。
- 运行之间可以扩大范围,绝不能缩小。 缩小范围以避开难以杀死的 变异体,就是操纵分数。如果工具通过持久配置文件限定范围,完成后 要扩大或移除该限制——遗留的狭窄范围会成为后续人员继承的排除项。
- 生产源代码只读。 测试、构建文件和工具配置可由你添加和修改——
报告会列出你改动的每个文件。唯一允许的源代码编辑,是用户在第 9 步
批准的排除注释。如果杀死某个变异体需要修改源代码,应将其作为问题
报告,而不是动手修改。如果某种语言将单元测试放在源文件内(Rust 的
#[cfg(test)] mod tests、Python doctests),你可以在该文件中添加 测试——绝不能触碰测试上方的代码,并在报告中说明你编辑了源文件及其 原因。 - 每次 Shell 调用只运行一条命令。 不要使用
for循环、&&链、;序列或通过管道传给tail:要求命令白名单授权的测试框架会对 每条复合命令请求许可,而非交互式运行会被拒绝。将基线运行两次,分两次 调用;较长输出请查看工具自带的摘要。 - 留在仓库内。 临时脚本、下载的本技能副本和等价性检查,都应放在 项目自己的忽略临时目录或你的 Agent 工作区中——不能放在同级目录或 系统临时目录。不要删除或改写仓库外的机器状态来让测试变得确定;如果 测试依赖此类状态,应将其作为问题报告。
分数无法反映的内容
变异测试会根据已编写的代码,评估现有测试。未实现的业务规则不会产生 变异体,因此完全缺失的功能也可能得到 100% 的分数。报告分数时,要明确 说明它未覆盖哪些风险——缺失代码这一盲点及其他局限,见 references/gotchas.md。
分数的可靠性取决于工具的变异算子。 这些工具并不等同:有些会逐个 边界地重写运算符,有些则只会替换整个函数体。较弱的变异算子集合即使 得到 100%,也可能掩盖一个未经测试的差一错误。报告分数时,必须注明 工具名称及其已知盲点,绝不能只报一个百分比——各工具的局限见 references/gotchas.md。
不存在普遍适用的好分数。应根据变更的代码设定门槛:其中的每个变异体都 已被杀死,或经用户批准后排除。
完成标准
- 在纳入范围的代码中,没有任何存活的变异体能够改变金额、权限或已存储的记录。
- 每个剩余的存活变异体,都有一条书面声明说明新的裁决者未能将其杀死。每条排除注释都经过裁决并获用户批准;未获任何人批准的声明会作为未决事项报告,不做排除。
- 每个存活的变异体,都用一句非测试人员也能理解的话加以解释。
- 你添加的每个测试都断言了一个业务结果,并且已在重新运行时确认能够杀死对应的变异体。
- 仓库干净:工具输出目录已删除或加入 gitignore,你写入配置的任何范围都已扩大或移除,报告列出了你添加或更改的每个文件。请在文件系统上检查,而不是使用
git status——有些工具会在自己的输出目录中写入.gitignore,因此git status会显示干净,但目录中仍会累积数百 MB 数据。请注明你选择保留的任何输出及其位置。
有未解释的存活变异体,即使分数很高,也不算完成。报告完整,并且用户选择接受了三个已解释、已排序的存活变异体,才算完成。
最后验证时间:2026-10-05,使用 Claude Code(claude-opus-5-5,无头模式,在 TicketBay 上运行 StrykerJS:裁决、审批和报告步骤)。工具设置最后验证时间:2026-09-02(在九个技术栈上进行实时测试)。