infection 是 php 生态中唯一成熟可用的变异测试工具,直接运行于 phpunit 测试套件之上;失败主因是 phpunit 未通过、测试入口缺失、超时设置过短、源码目录配置错误或断言薄弱。

Infection 不是“变异测试的 PHP 实现”,而是 PHP 生态中唯一成熟可用的变异测试工具 —— 它不提供 API 供你“实现”,而是直接运行在现有 PHPUnit 测试套件之上。如果你的测试没跑过、覆盖率低、或断言太弱,Infection 会立刻暴露出来,而不是让你“实现”一个抽象概念。
为什么 infection/infection 一跑就挂:常见失败原因
多数人第一次执行 infection 时看到 ERROR: No tests executed 或 Timed out while running mutants,根本不是配置写错了,而是底层条件不满足:
- PHPUnit 必须能独立成功运行(
vendor/bin/phpunit零失败),Infection 不接管测试逻辑,只复用你的phpunit.xml配置 - 项目必须有可执行的测试入口(比如
tests/下存在至少一个*Test.php文件,且类名匹配*Test后缀) - 默认超时是 10 秒/变异体,若单个测试本身慢(比如含 I/O、DB 连接),Infection 会直接跳过或报
timed out—— 此时要加--timeout=30,而非改代码 -
infection.json中的source.directories必须精确指向被测源码目录(如["src"]),漏掉会导致 0 变异体生成
infection --test-framework-options="--group=unit" 怎么传参才生效
Infection 自身不解析 PHPUnit 的 --group、--filter 等参数,所有这些必须通过 --test-framework-options 委托给 PHPUnit 进程。关键点在于:
- 参数值需整体包裹在双引号内,避免 shell 拆分(例如
--test-framework-options="--group=auth --verbose") - 不能混用短选项和长选项(
-v在 PHPUnit 8+ 已废弃,Infection 下必须用--verbose) - 如果 PHPUnit 配置里已定义
<groups></groups>,命令行传入的--group会覆盖它;反之,不传则完全依赖配置文件 - 想只测某几个类?用
--filter="MyServiceTest::testHandlesNullInput",但注意 Infection 会为整个MyService.php文件生成变异体,哪怕只跑一个方法
覆盖率高 ≠ 变异分数高:为什么 mutant_score 卡在 30%
100% 行覆盖率只是底线,Infection 看的是“测试能否杀死变异体”。以下情况会让分数骤降:
- 断言太弱:比如只断言
$this->assertTrue($result),而变异体把true变成false后,测试仍通过(因为 $result 本来就是 false)—— 改成$this->assertSame(true, $result)才能捕获 - 未覆盖边界值:
if ($x > 0)被变异为if ($x >= 0),但测试只传了1和-1,漏了0—— 补0就能杀掉这个变异体 - 异常测试缺失:
throw new InvalidArgumentException()被变成throw new RuntimeException(),但没写@expectedException InvalidArgumentException或expectException(),导致变异体存活 - 静态方法/全局状态干扰:Infection 运行多个子进程,若测试依赖
date_default_timezone_set()或$_SERVER修改,可能在某些进程中失效,造成非确定性存活
真正卡住的地方往往不是工具不会用,而是得回头重审那条“看似正确”的断言 —— 它到底在验证行为,还是只在碰巧通过?
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











