PHP测试:从入门到精通的单元测试、集成测试与端到端测试

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

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

开始课程
```html

引言:为什么 PHP 测试不是可选项,而是标准

在现代 PHP 开发中,测试早已不再是“锦上添花”,而是生产项目的必要环节。没有测试覆盖的代码,你可能会将错误发布到生产环境,这可能会给企业带来金钱和声誉上的损失。在本文中,我们将探讨三个测试级别:unit(单元测试)、integration(集成测试)和 e2e(端到端测试)。你将了解它们的区别、使用哪些工具(PHPUnit、Pest、Laravel Dusk、Codeception),以及如何为你的 PHP 项目构建正确的测试策略。

1. 单元测试:测试隔离的代码单元

单元测试在隔离外部依赖(数据库、API、文件系统)的情况下,测试单个类、函数或方法的工作情况。这是最快、成本最低的测试类型。PHP 中的主要工具是 PHPUnit(事实标准)及其“时尚”替代品 Pest。

1.1. 安装 PHPUnit 并编写第一个测试

通过 Composer 安装 PHPUnit:

composer require --dev phpunit/phpunit

为一个简单的计算器类创建测试:

<?php
// src/Calculator.php
class Calculator {
    public function add(int $a, int $b): int {
        return $a + $b;
    }
}

// tests/CalculatorTest.php
use PHPUnit\\Framework\\TestCase;

class CalculatorTest extends TestCase {
    public function testAddition(): void {
        $calc = new Calculator();
        $result = $calc->add(2, 3);
        $this->assertEquals(5, $result);
    }
}

运行测试:

./vendor/bin/phpunit tests

1.2. 使用 Mock 对象

为了隔离被测试的类,我们使用 mock 来“替换”其依赖项。以用户仓库为例:

<?php
class UserService {
    private UserRepository $repo;
    public function __construct(UserRepository $repo) {
        $this->repo = $repo;
    }
    public function getUserName(int $id): string {
        $user = $this->repo->findById($id);
        return $user ? $user->getName() : 'Guest';
    }
}

// 在测试中
$mockRepo = $this->createMock(UserRepository::class);
$mockRepo->method('findById')
    ->with(1)
    ->willReturn(new User('Alice'));

$service = new UserService($mockRepo);
$this->assertEquals('Alice', $service->getUserName(1));

最佳实践: 为所有业务逻辑编写单元测试。它们应在几秒内执行完毕,且不依赖于环境。

2. 集成测试:测试与数据库、API 和外部服务的交互

集成测试检查你的类如何与真实组件(如数据库、缓存、外部 HTTP 服务)协同工作。它们比单元测试慢,但能让你更有信心系统作为一个整体正常运行。

2.1. 测试数据库操作(Laravel + RefreshDatabase)

如果你使用 Laravel,借助 RefreshDatabase trait,集成测试会变得非常简单:

<?php
namespace Tests\\Feature;

use Tests\\TestCase;
use Illuminate\\Foundation\\Testing\\RefreshDatabase;

class UserRegistrationTest extends TestCase {
    use RefreshDatabase;

    public function test_user_can_register(): void {
        $response = $this->post('/register', [
            'name' => 'John',
            'email' => 'john@example.com',
            'password' => 'secret123',
        ]);

        $response->assertStatus(302); // 成功后重定向
        $this->assertDatabaseHas('users', [
            'email' => 'john@example.com'
        ]);
    }
}

RefreshDatabase trait 会在测试前自动迁移数据库,并在测试后回滚所有更改。

```

博客

书籍推荐