PHP 测试:Unit、Integration、E2E — 完整指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么需要在 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)');                

博客

书籍推荐