Infection 变异分数计算

解读

在国内 PHP 技术面试中,单元测试覆盖率已逐渐成为“标配”指标,但“覆盖率够了,Bug 还是很多”是面试官最爱追问的痛点。Infection 作为 PHP 生态唯一成熟、落地案例多的“变异测试(Mutation Testing)”工具,通过故意植入“变异算子”(如把 == 改成 !=、把 ++$i 改成 --$i 等),检验测试用例能否“杀死”这些变异体,从而量化测试的“有效性”。
“变异分数”是 Infection 最核心的输出指标,直接决定测试用例是否真正值得信赖。面试官问“怎么算”,不仅是考察公式记忆,更是考察你对指标背后“成本—收益”的理解、对工程化落地的权衡,以及能否把结论翻译成团队能执行的动作(例如 CI 门禁、增量 MR 策略、性能调优等)。回答时务必把“公式 → 参数 → 调优 → 门禁”讲全,才能体现资深后端工程师的闭环思维。

知识点

  1. 变异测试基础概念:变异体(Mutant)、杀死(Killed)、存活(Escaped)、等价(Equivalent)、超时(Timeout)。
  2. Infection 的三种运行模式:常规模式(默认)、仅覆盖模式(--only-covered)、最小变异集模式(--min-msi 配合 --filter)。
  3. 指标公式:
    MSI(Mutation Score Indicator) = (Killed + Timeout + Timed Out) / (Total Mutants − Equivalent) × 100%
    Covered Code MSI = (Killed + Timeout) / (Covered Mutants − Equivalent) × 100%
  4. 国内落地常见阈值:
    新服务 MSI ≥ 60% 可进主干,老服务渐进式提升到 45%;
    增量文件要求 Covered MSI ≥ 80%,防止“破窗”。
  5. 性能调优手段:
    使用 paratest 做多进程执行、开启 opcache.enable_cli=1、把 memory_limit 调到 512M 以上;
    等价变异体自动检测(--min-msi 配合 roave/infection-static-analysis-plugin)减少误报。
  6. CI 集成要点:
    在 GitLab-CI 中把 Infection 跑在 test:mutation 阶段,产物 infection.logjunit.xml 一并归档;
    合并请求评论机器人读取 infection.jsonmsi 字段,低于阈值自动 block。
  7. 与业务指标挂钩:
    把“线上缺陷密度”与“MSI 趋势”做双轴图,每月复盘,证明变异测试对缺陷率有显著下降(国内甲方最认这种量化汇报)。

答案

变异分数(MSI)由 Infection 在 infection.json 中自动输出,核心公式:
MSI = (被杀死的变异体数 + 超时变异体数) ÷ (总变异体数 − 等价变异体数) × 100%

示例:
某支付模块共生成 2 000 个变异体,其中 160 个被判定为等价,运行后 1 280 个被杀死、80 个超时。
MSI = (1280 + 80) ÷ (2000 − 160) × 100% = 1360 ÷ 1840 × 100% ≈ 73.9%

国内工程实践:

  1. 先在 phpunit.xml 中开启 <coverage><include><directory suffix=".php">src</directory></include></coverage>,保证覆盖率数据完整;
  2. 运行 infection --only-covered --threads=8 --min-msi=60,把 MSI 低于 60% 的 MR 直接拒绝;
  3. 对历史大仓库采用“增量模式”:infection --git-diff-lines=origin/main --ignore-msi-with-no-mutations,只检测变更行,5 分钟内跑完,解决“全量跑太久”的痛点;
  4. 把 MSI 趋势接入 Prometheus,通过 Grafana 面板每周邮件提醒,形成“测试有效性”可视化,方便技术总监在季度汇报时展示“质量内建”成果。

拓展思考

  1. 等价变异体识别一直是变异测试的“阿喀琉斯之踵”。PHP 社区正在尝试结合 Psalm、PHPStan 的静态分析,自动过滤“不可达”变异体;你可以调研 roave/infection-static-analysis-plugin,并给出落地 PR 案例。
  2. 当 MSI 已达标但线上仍出 Bug,往往是“业务语义”层面缺失。可引入“属性级变异”(如把订单状态常量 STATUS_PAID 改成 STATUS_CANCELLED),让变异体更贴近真实业务错误,考验你对“业务规则测试”的理解。
  3. 大型 SaaS 场景下,微服务数量多、全量跑 Infection 成本高。可设计“分层门禁”:
    单元层 MSI ≥ 70% → 集成层变异(契约测试层)≥ 50% → 端到端变异(UI 自动化)≥ 30%,形成“金字塔”有效性模型,既保证质量又控制 CI 预算。