引言:为什么需要在 PHP 中进行测试
PHP 开发不仅仅是编写"能运行"的代码。在现代 Web 开发中,应用程序的稳定性和可预测性至关重要。没有测试,代码中的任何更改都可能破坏已有的功能,而手动查找错误则会变成一场无休止的"捉虫游戏"。这正是测试发挥作用的地方。
在本文中,我们将解析 PHP 应用程序测试的三个关键层级:单元测试、集成测试和端到端测试。您将了解它们的区别、应使用哪些工具,以及如何为项目构建高效的测试策略。
单元测试:测试金字塔的基石
单元测试是最快速且隔离性最高的测试层级。其目标是在隔离外部依赖(数据库、文件系统、API)的情况下,验证单个"单元"(类、方法或函数)的运行。
PHP 工具
最流行的框架是 PHPUnit。另一种更现代的替代方案是 Pest,它提供了更简洁的语法。
PHPUnit 单元测试示例
假设我们有一个简单的计算器类:
<?php
// src/Calculator.php
class Calculator
{
public function add(int $a, int $b): int
{
return $a + $b;
}
public function divide(int $a, int $b): float
{
if ($b === 0) {
throw new \\InvalidArgumentException('Division by zero');
}
return $a / $b;
}
}
对应的测试如下:
<?php
// tests/Unit/CalculatorTest.php
use PHPUnit\\Framework\\TestCase;
class CalculatorTest extends TestCase
{
private Calculator $calculator;
protected function setUp(): void
{
$this->calculator = new Calculator();
}
public function testAddReturnsCorrectSum(): void
{
$result = $this->calculator->add(2, 3);
$this->assertEquals(5, $result);
}
public function testDivideThrowsExceptionOnZero(): void
{
$this->expectException(\\InvalidArgumentException::class);
$this->calculator->divide(10, 0);
}
}
单元测试的关键原则:
- 隔离性:所有外部依赖(数据库、API)均通过 mock 或 stub 替换。
- 速度:执行时间在毫秒级。
- 验证单一具体场景(每个测试一个断言是良好实践)。
运行命令:./vendor/bin/phpunit tests/Unit
集成测试:验证交互
集成测试提升了一个层级。它们验证系统不同部分如何协同工作:类与数据库、控制器与模型、服务与外部 API(真实或模拟)。
何时需要集成测试?
- 验证 SQL 查询的正确性。
- 确认应用程序各层之间的连接(例如 Repository + Entity)。
- 测试中间件或事件。
示例:使用真实数据库测试 Repository
集成测试通常使用测试数据库(内存中的 SQLite 或 PostgreSQL 中的事务)。以下是使用 PHPUnit 和数据库重置 trait 的示例:
<?php
// tests/Integration/UserRepositoryTest.php
use PHPUnit\\Framework\\TestCase;
class UserRepositoryTest extends TestCase
{
private PDO $pdo;
private UserRepository $repository;
protected function setUp(): void
{
// 创建内存 SQLite 以实现隔离
$this->pdo = new PDO('sqlite::memory:');
$this->pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)');