PHP 怎么Infection

wen PHP项目 3

PHP 项目怎么用 Infection?— 从零开始的 Mutation Testing 实战指南


目录导读(Table of Contents)

  1. 什么是 Infection?为什么 PHP 开发者需要它?
  2. 核心概念速览:Mutation Testing(变异测试)与代码覆盖率的关系
  3. 环境准备:安装 Infection(Composer 与 Phar 双方案)
  4. 实战配置:infection.json5 文件深度解析(含超集配置)
  5. 运行你的第一次变异测试:命令行参数与 CI 集成技巧
  6. 解读报告:MSI(变异得分指数)如何指导你编写更健壮的测试?
  7. 常见坑与性能优化:如何让 Infection 跑得飞快?
  8. 高频问答(FAQ):解决你 90% 的疑惑

什么是 Infection?为什么 PHP 开发者需要它?

PHP 怎么Infection

很多 PHP 开发者都有这样的经历:代码覆盖率(Code Coverage)达到了 90%,但上线后依然出现 bug,原因很简单——覆盖率只能证明“代码被执行了”,无法证明“测试真的验证了行为”,你的测试可能调用了 if 语句的分支,但从未断言 else 分支的结果。

Infection 是一款专为 PHP 设计的 Mutation Testing(变异测试)框架,它的核心逻辑是:故意往你的源码里“投毒”(例如把 改成 ,或把 改成 ),然后运行你的测试套件,如果测试没有失败,说明这个“变异体”存活了,你的测试存在漏洞。

简而言之:Infection 是测试的试金石,它能找出那些“假绿”的测试用例。

核心概念速览:Mutation Testing 与代码覆盖率的关系

  • 变异体(Mutant):对源代码进行微小改动后的版本。
  • Killed(被杀死的):测试发现了变异,导致测试失败。
  • Escaped(逃脱的):测试未发现变异,测试依然通过——这是最危险的信号。
  • MSI(Mutation Score Indicator):变异得分 = 被杀死的变异数 / 总变异数。80% 以上的 MSI 是健康的标准,90% 以上则为优秀。

关键区别:代码覆盖率(如 Xdebug)是“静态”指标,Mutation Testing 是“动态”验证。覆盖率是底线,MSI 是质量天花板。

环境准备:安装 Infection(Composer 与 Phar 双方案)

方案 A:Composer 全局安装(推荐用于 CI 环境)

composer global require infection/infection
export PATH="$PATH:$HOME/.composer/vendor/bin"

方案 B:Phar 包(适合隔离环境)

wget https://github.com/infection/infection/releases/latest/download/infection.phar
chmod +x infection.phar
sudo mv infection.phar /usr/local/bin/infection

安装后验证:

infection --version

实战配置:infection.json5 文件深度解析

在项目根目录创建 infection.json5 文件:

{
    "$schema": "https://raw.githubusercontent.com/infection/infection/main/resources/schema.json",
    "source": {
        "directories": [   // 指定要被变异的源码目录
            "src"
        ],
        "exclude": [       // 排除模板或生成代码
            "src/Generated"
        ]
    },
    "timeout": 10,         // 单个测试超时时间(秒)
    "logs": {
        "text": "build/infection.log",  // 文本日志
        "summary": "build/infection-summary.txt"
    },
    "mutators": {
        "global-ignore": [  // 忽略某些特定规则,例如忽略数组弹栈操作
            "ArrayPop"
        ],
        "@default": true    // 启用所有默认变异器
    },
    "minMsi": 85,          // 最低 MSI 要求,低于此值 CI 会失败
    "minCoveredMsi": 90    // 针对已覆盖代码的最低 MSI
}

高级配置技巧:如果项目使用了 Laravel,建议在 bootstrap 中添加 "extensions": ["infection/extension-installer"] 来自动适配框架。

运行你的第一次变异测试:命令行参数与 CI 集成技巧

基础运行(默认会询问是否生成配置,此时直接传递配置即可):

infection --configuration=infection.json5 --threads=4

常用参数:

  • --threads=4:并行执行,大幅提升速度(CPU 核心数减 1)。
  • --only-covered:只对代码覆盖率已达到的代码行进行变异,节省时间。
  • --show-mutations:显示具体的变异细节(如哪行被改成了什么)。
  • --git-diff-lines:只变异本次 Git 提交中修改的行(配合 CI 分支检查)。

CI 集成示例(GitHub Actions)

- name: Run Infection
  run: |
    vendor/bin/infection --min-msi=80 --min-covered-msi=85

解读报告:MSI 如何指导你编写更健壮的测试?

运行后,打开 build/infection.log,你会看到类似输出:

Escaped mutants:
=================
1) /path/to/src/Calculator.php:12    [M] PlusMinus (将 + 改为 -)
   --- Original
   +++ New
   @@ @@
    public function add(int $a, int $b): int
   -    return $a + $b;
   +    return $a - $b;

应对策略

  • 如果出现 Escaped,你的测试缺少断言,例如上述场景,测试只检查了返回值是否为正数,但没检查具体数值,修复方法:断言 assertEquals(3, $calculator->add(1, 2))
  • MSI 低于设定值,优先处理 Escaped 数量最多的文件,而不是盲目追求覆盖率。

常见坑与性能优化:如何让 Infection 跑得飞快?

  • 坑 1:慢测试 → 优化:在 phpunit.xml 中开启 processIsolationpreserveGlobalStatefalse
  • 坑 2:变异爆炸 → 优化:先使用 --only-covered 或结合 --git-diff-lines 缩小范围。
  • 坑 3:MongoDB/SQLite 扩展问题 → 优化:在 CI 中启用 --skip-initial-tests 跳过初始测试,或使用 extensions 配置忽略特定变异器(如 DateTime 相关)。

性能史诗级优化:在项目根目录添加 .infection 缓存文件(默认自动生成),并确保 vendor/autoload.php 使用 --classmap-authoritative 生成优化后的自动加载机制。

高频问答(FAQ)

Q1:Infection 和 PHPStan/Psalm 有什么区别?
A:PHPStan 是静态分析(找代码逻辑错误),Infection 是动态验证(找测试漏洞),两者互补,不冲突。

Q2:我的测试很完善,MSI 应该多少合适?
A:对于库或核心业务模块,建议不低于 80%;对于控制器或模板代码,60% 即可接受。关键看业务价值,不要为纯 getter/setter 追求 100%

Q3:运行时间太长,能中断吗?
A:可以,Infection 支持断点续跑,完整跑完后进程会退出,中途 Ctrl+C 不会损坏任何数据。

Q4:有没有替代品?
A:有。Humbug 是老牌工具,但已停止维护。Deptrac 是架构层面的工具,与 Infection 不同层级。在 PHP 8.1+ 生态中,Infection 是事实标准

Q5:如何排除某些特定函数不被变异?
A:在 infection.json5mutators 块中声明 "global-ignore": ["DateModify"] 即可。


Infection 不是银弹,但它是 PHP 工程质量的重要补充,当你把 MSI 从 40% 提升到 85% 时,你会发现那些曾经“看起来没问题”的测试,其实在静默地漏掉真正的 bug,从今天起,把它加入你的 CI 流程吧。

抱歉,评论功能暂时关闭!