概述
覆盖率衡量的是“这行代码被执行过”,而不是“这行代码出错时会被发现”。变异测试把差别具体化:它小幅修改代码,例如翻转比较符、删除返回值,然后看是否有测试失败。存活的变异体意味着测试执行了这一行,却从未断言这一行所控制的行为。
步骤
- 安装核心包与 Vitest 运行器:
pnpm add -D @stryker-mutator/core @stryker-mutator/vitest-runner
- 限制变异范围到关键源码,并设置会让构建失败的阈值:
{
"testRunner": "vitest",
"mutate": ["src/pricing/**/*.ts", "!src/pricing/**/*.test.ts"],
"thresholds": { "high": 85, "low": 70, "break": 70 }
}
- 运行
pnpm stryker run,先阅读存活变异体,再决定改测试还是标注等价变异体。 - 对确实无法改变行为的变异体使用
// Stryker disable next-line ...并写明原因,禁止无说明的忽略。 - 在合并请求中使用
--incremental,把全量运行放到夜间任务。
验证
首次运行应输出类似 78.4%(142 个被杀、39 个存活)的分数以及 HTML 报告。补上边界断言后分数应上升;当分数低于 70 时命令应以非零状态退出。重复运行加 --incremental 应在数分钟内完成,而不是重新跑一小时。
注意事项
- 变异测试很慢,只应用于金额、权限、状态机等关键模块,不要全仓运行。
- 分数是测试强度的近似指标,不代表业务正确性。
- 等价变异体无法被杀死,会抬高存活数,不必追求 100%。
- 第一天就设高阈值会让构建长期失败,应先测量再把
break设在当前分数略下方并逐步提高。 - 运行器不要包含端到端与集成测试,否则会变慢甚至挂起;应指定独立的 Vitest 配置。