本文目录导读:

在 PHP 中进行变异测试,主要是通过引入变异测试工具,在代码中注入缺陷(变异体),然后运行测试套件来检测现有测试是否能够发现这些变异,如果测试未能发现某个变异,说明该变异的对应代码段缺乏足够的测试覆盖。
以下是 PHP 变异测试的完整指南:
主要的变异测试工具
Infection(最流行)
Infection 是目前 PHP 生态中最成熟、最活跃的变异测试框架。
Humbug
Humbug 是较早的变异测试工具,但已不再维护,现在推荐使用 Infection。
安装与配置 Infection
安装
使用 Composer 安装:
composer require --dev infection/infection
基本配置
创建 infection.json 配置文件:
{
"$schema": "https://raw.githubusercontent.com/infection/infection/master/resources/schema.json",
"source": {
"directories": [
"src"
]
},
"timeout": 10,
"logs": {
"text": "infection.log"
},
"mutators": {
"global-ignores": [
"src/Adapters/"
]
}
}
或者使用命令行直接指定源目录:
vendor/bin/infection --source=src --test-framework=phpunit --covered-only
运行变异测试
基本运行
vendor/bin/infection
高级运行选项
# 仅测试已覆盖的代码(更快) vendor/bin/infection --covered-only # 指定测试框架(PHPUnit 或 PHPSpec) vendor/bin/infection --test-framework=phpunit # 限制变异数量(加速测试) vendor/bin/infection --max-mutations=100 # 跳过未覆盖代码 vendor/bin/infection --skip-covered
理解变异测试结果
运行后会生成类似以下的输出:
Processing source code files: 50
Processing test classes: 100
...
2 mutations were generated:
1) /path/to/file.php:76 [M] PublicVisibility
--- Original
+++ New
@@ @@
- public function calc()
+ protected function calc()
Mutation was not detected by tests
结果类型
- Killed:测试发现了变异,说明该代码被充分测试
- Escaped:测试未发现变异,需要增加测试
- Uncovered:变异代码从未被执行,需要添加测试
- Timeout:测试运行超时
变异测试指标
变异测试的核心指标是 MSI(Mutation Score Indicator):
MSI = (Killed Mutants / Total Mutants) × 100
目标:通常建议 MSI 达到 80% 以上。
在 CI/CD 中集成
在 GitLab CI 或 GitHub Actions 中:
# GitHub Actions 示例
name: Mutation Testing
on: [push]
jobs:
mutation-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
- run: composer install
- run: vendor/bin/infection --min-msi=80 --covered-only
env:
CI: true
实用技巧
忽略不可测试的代码
{
"source": {
"directories": [
"src"
],
"exclude": [
"src/Config",
"src/Models/Entity"
]
},
"mutators": {
"global-ignores": [
"src/Kernel.php",
"src/Doctrine/"]
}
}
只测试特定类
# 只测试 User 类的变异 vendor/bin/infection --filter=User
提高运行速度
- 使用
--threads=4并行执行 - 使用
--covered-only只变异已覆盖代码 - 限制最大变异数:
--max-mutations=500
与 PHPUnit 协作
确保 PHPUnit 测试快速执行,可以设置 --coverage:
# 生成覆盖率并只测试剩余代码 vendor/bin/infection --coverage=coverage --min-msi=85
完整配置文件示例
{
"$schema": "https://raw.githubusercontent.com/infection/infection/master/resources/schema.json",
"source": {
"directories": ["src/"]
},
"logs": {
"text": "reports/infection.txt",
"html": "reports/infection.html",
"github": "true"
},
"mutators": {
"global-ignores": [
"src/Entity/*",
"src/DTO/*"
],
"@default": true,
"CastInt": false,
"DecrementInteger": false
},
"min-msi": 85,
"min-covered-msi": 90,
"threads": 4,
"timeout": 10,
"executableFinder": {
"phpunit": "vendor/bin/phpunit"
}
}
常见问题与解决
| 问题 | 解决方案 |
|---|---|
| 测试太慢 | 增加 --threads,使用 --covered-only |
| 变异逃脱太多 | 审查被漏掉的测试,增加边界条件测试 |
| 误报变异 | 使用 global-ignores 忽略特定代码 |
| CI 内存不足 | 设置 MEMORY_LIMIT=-1 或增加 PHP 内存限制 |
| 覆盖率高但 MSI 低 | 检查测试断言是否过于宽泛 |
推荐工作流程
# 1. 首先运行正常测试确保通过 composer test # 2. 生成覆盖率报告 vendor/bin/phpunit --coverage-xml=coverage/coverage-xml # 3. 运行变异测试 vendor/bin/infection --coverage=coverage --min-msi=85 # 4. 查看报告并补充测试 open reports/infection.html
变异测试是提升测试质量的重要手段,通过 Infection,你可以:
- 准确衡量测试的有效性
- 发现测试盲区
- 不断提高 MSI 直至达到理想水平(80%+)
- 防止回归测试不充分的问题
建议先从关键业务逻辑代码开始,逐步推广到全项目,因为变异测试运行时间较长(可能是普通测试的 10-20 倍),需要对速度和覆盖进行平衡。