变异测试
变异测试(Mutation Testing)通过往源代码里自动注入一个个微小的人为缺陷(称为 mutant / 变异体,例如把 + 改成 -、< 改成 <=、true 改成 false),再用你现有的测试套件去跑——能让某个测试失败的变异体被「杀死」(detected),改坏后测试照样全绿的则「存活」(survived),暴露出测试盲区。它衡量的不是「代码跑没跑到」(那是覆盖率),而是「测试的断言够不够强、能不能抓到 bug」,因此俗称「测试你的测试」。前端主流工具是 StrykerJS,原生支持 Vitest / Jest 等运行器,并能用 thresholds.break 在 CI 里卡住测试质量回退。
评价
优点
- 抓出「假绿」测试:覆盖率 100% 也可能只是「调用了代码、没校验返回值」,变异测试能直接揪出这种缺断言的无效测试
- 盲区清单可定位:HTML 报告把每个 survived / no coverage 变异体高亮到具体行列,等于一份「该补哪些断言」的待办清单
- 指标客观:mutation score =
detected / valid,把「测得准不准」从主观感觉变成可观测数字 - CI 门禁:
thresholds.break不达标即退出码 1,阻止合并,防止测试质量悄悄退化 - 算子丰富 + 零侵入:内置 15 类变异算子(算术 / 边界 / 布尔 / 逻辑 / 可选链 …),无需改业务代码,glob 圈定范围即可跑
缺点
- 慢一两个数量级:本质是「N 个变异体 × 测试套件」,一个文件能生成几十上百个变异体,全仓跑几乎不可行——必须靠
perTest+ 增量 + 缩小mutate+ 并发控本 - 100% 往往不可达:存在等价变异体(变异后功能与原码完全等价,任何测试都杀不死),盲目追 100% 会逼出无意义测试
- 不适合 UI / 样板:纯展示组件、自动生成代码跑出来多是低价值噪音,应排除出范围
- 门禁默认不生效:
thresholds.break默认null,不显式设值时永远不会让 CI 失败(高频陷阱)