本文目录导读:

我来详细解释PHP项目中的代码覆盖率和变异测试。
代码覆盖率 (Code Coverage)
什么是代码覆盖率
代码覆盖率衡量测试用例执行过程中,源代码被覆盖的比例。
覆盖率类型
// 示例代码
class Calculator {
public function add($a, $b) {
return $a + $b; // 行覆盖率
}
public function divide($a, $b) {
if ($b == 0) { // 分支覆盖率
return "error";
}
return $a / $b;
}
public function process($value) {
if ($value > 0) { // 条件覆盖率
return "positive";
} elseif ($value < 0) {
return "negative";
}
return "zero";
}
}
PHP代码覆盖率工具
PHPUnit + php-code-coverage
<!-- phpunit.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<phpunit>
<filter>
<whitelist>
<directory>src</directory>
<exclude>
<directory>src/Exceptions</directory>
</exclude>
</whitelist>
</filter>
<coverage>
<include>
<directory suffix=".php">src</directory>
</include>
<report>
<html outputDirectory="coverage" />
<clover outputFile="coverage.xml" />
</report>
</coverage>
</phpunit>
运行测试并生成覆盖率报告
# 生成覆盖率报告 vendor/bin/phpunit --coverage-html coverage/ vendor/bin/phpunit --coverage-clover coverage.xml # 设置最低覆盖率 vendor/bin/phpunit --coverage-html coverage/ --coverage-text
覆盖率配置文件
<!-- phpunit.xml.dist -->
<phpunit>
<coverage>
<report>
<html outputDirectory="build/coverage"/>
<text outputFile="php://stdout"/>
</report>
<clover outputFile="build/logs/clover.xml"/>
<crap4j outputFile="build/logs/crap4j.xml" threshold="50"/>
</coverage>
</phpunit>
实际应用示例
// src/UserService.php
class UserService {
private $db;
public function __construct(Database $db) {
$this->db = $db;
}
public function createUser($name, $email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Invalid email");
}
if ($this->userExists($email)) {
return false;
}
return $this->db->insert('users', [
'name' => $name,
'email' => $email
]);
}
private function userExists($email) {
return $this->db->query(
"SELECT COUNT(*) FROM users WHERE email = ?",
[$email]
) > 0;
}
}
// tests/UserServiceTest.php
class UserServiceTest extends TestCase {
public function testCreateUserWithValidData() {
$db = $this->createMock(Database::class);
$service = new UserService($db);
$db->method('query')->willReturn(0);
$db->method('insert')->willReturn(1);
$result = $service->createUser('John', 'john@example.com');
$this->assertTrue($result);
}
public function testCreateUserWithInvalidEmail() {
$this->expectException(InvalidArgumentException::class);
$db = $this->createMock(Database::class);
$service = new UserService($db);
$service->createUser('John', 'invalid-email');
}
}
变异测试 (Mutation Testing)
什么是变异测试
变异测试通过引入小型代码变更(变异),验证测试用例能否检测到这些变更。
常见变异操作
// 原始代码
function max($a, $b) {
if ($a > $b) {
return $a;
}
return $b;
}
// 变异操作示例
function max($a, $b) {
if ($a > $b) { // 条件反转变异:if ($a < $b)
return $a; // 返回值变异:return $b;
}
return $b; // 返回值变异:return $a;
}
PHP变异测试工具
Infection (推荐)
# 安装
composer require --dev infection/infection
# 配置 infection.json
{
"source": {
"directories": ["src"]
},
"mutators": {
"@default": true,
"NumberMutator": false,
"ConditionalBoundary": false
},
"logs": {
"text": "infection.log",
"html": "infection.html",
"summary": "infection-summary.txt"
},
"minMsi": 80,
"minCoveredMsi": 90
}
运行变异测试
# 基本运行 vendor/bin/infection # 指定配置 vendor/bin/infection --configuration=infection.json # 设置线程数 vendor/bin/infection --threads=4 # 仅测试特定文件 vendor/bin/infection --filter=Calculator.php
变异测试实战
// src/OrderProcessor.php
class OrderProcessor {
public function calculateDiscount($total, $isMember) {
if ($total > 100 && $isMember) {
return $total * 0.9; // 10% discount
}
if ($total > 200) {
return $total * 0.85; // 15% discount
}
return $total;
}
}
// tests/OrderProcessorTest.php
class OrderProcessorTest extends TestCase {
public function testMemberDiscount() {
$processor = new OrderProcessor();
$result = $processor->calculateDiscount(150, true);
$this->assertEquals(135, $result); // 150 * 0.9
}
public function testNonMemberDiscount() {
$processor = new OrderProcessor();
$result = $processor->calculateDiscount(250, false);
$this->assertEquals(212.5, $result); // 250 * 0.85
}
public function testNoDiscount() {
$processor = new OrderProcessor();
$result = $processor->calculateDiscount(50, false);
$this->assertEquals(50, $result);
}
}
覆盖率与变异测试对比
| 特性 | 代码覆盖率 | 变异测试 |
|---|---|---|
| 目的 | 测试代码执行情况 | 测试测试质量 |
| 衡量标准 | 覆盖的行/分支百分比 | 检测到的变异比例 |
| 执行速度 | 快 | 慢(需要多次运行) |
| 结果解读 | 直观易懂 | 需要分析死亡/存活的变异 |
| 资源消耗 | 低 | 高 |
最佳实践
代码覆盖率目标
<!-- phpunit.xml -->
<phpunit>
<coverage>
<report>
<html outputDirectory="coverage"/>
</report>
</coverage>
<!-- 设置覆盖率阈值 -->
<groups>
<include>
<group>critical</group>
</include>
</groups>
</phpunit>
CI/CD集成
# .github/workflows/tests.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run tests with coverage
run: |
vendor/bin/phpunit --coverage-clover=coverage.xml
vendor/bin/infection --min-msi=70 --min-covered-msi=80
提升覆盖率策略
// 测试边界值
public function testBoundaryValues() {
$processor = new OrderProcessor();
// 边界值测试
$this->assertEquals(100, $processor->calculateDiscount(100, false));
$this->assertEquals(101, $processor->calculateDiscount(101, false));
$this->assertEquals(90, $processor->calculateDiscount(100, true));
$this->assertEquals(90.9, $processor->calculateDiscount(101, true));
// 负数测试
$this->assertEquals(0, $processor->calculateDiscount(0, false));
}
代码覆盖率和变异测试是质量保证的重要工具:
- 代码覆盖率:确保测试覆盖所有代码路径
- 变异测试:验证测试用例的实际有效性
建议组合使用这两个工具来构建高质量的PHP测试套件。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播