← 中文资源

简体中文版本

为关键模块引入变异测试

在金额与权限相关代码上运行 Stryker,设置变异分数阈值,并针对存活变异体补强断言。

阅读英文原文

概述

覆盖率衡量的是“这行代码被执行过”,而不是“这行代码出错时会被发现”。变异测试把差别具体化:它小幅修改代码,例如翻转比较符、删除返回值,然后看是否有测试失败。存活的变异体意味着测试执行了这一行,却从未断言这一行所控制的行为。

步骤

  1. 安装核心包与 Vitest 运行器:
pnpm add -D @stryker-mutator/core @stryker-mutator/vitest-runner
  1. 限制变异范围到关键源码,并设置会让构建失败的阈值:
{
  "testRunner": "vitest",
  "mutate": ["src/pricing/**/*.ts", "!src/pricing/**/*.test.ts"],
  "thresholds": { "high": 85, "low": 70, "break": 70 }
}
  1. 运行 pnpm stryker run,先阅读存活变异体,再决定改测试还是标注等价变异体。
  2. 对确实无法改变行为的变异体使用 // Stryker disable next-line ... 并写明原因,禁止无说明的忽略。
  3. 在合并请求中使用 --incremental,把全量运行放到夜间任务。

验证

首次运行应输出类似 78.4%(142 个被杀、39 个存活)的分数以及 HTML 报告。补上边界断言后分数应上升;当分数低于 70 时命令应以非零状态退出。重复运行加 --incremental 应在数分钟内完成,而不是重新跑一小时。

注意事项

  • 变异测试很慢,只应用于金额、权限、状态机等关键模块,不要全仓运行。
  • 分数是测试强度的近似指标,不代表业务正确性。
  • 等价变异体无法被杀死,会抬高存活数,不必追求 100%。
  • 第一天就设高阈值会让构建长期失败,应先测量再把 break 设在当前分数略下方并逐步提高。
  • 运行器不要包含端到端与集成测试,否则会变慢甚至挂起;应指定独立的 Vitest 配置。