Pest 测试框架快速入门
High Contrast
Dark Mode
Light Mode
Sepia
Forest
1 min read148 words

Pest 测试框架快速入门

Pest 是 Laravel 社区现在最推荐的 PHP 测试框架。它在 PHPUnit 的基础上提供更简洁的语法——测试代码像描述文档一样可读。Laravel 11 的默认测试套件已经是 Pest。


安装

# Laravel 11 新项目已内置 Pest
composer require pestphp/pest --dev
php artisan pest:install
# 运行所有测试
php artisan test
# 或者:
./vendor/bin/pest
# 只运行某个文件
./vendor/bin/pest tests/Feature/TaskTest.php
# 只运行包含关键词的测试
./vendor/bin/pest --filter="can create a task"
# 并发执行(加速)
./vendor/bin/pest --parallel

Pest 语法基础

// tests/Feature/TaskTest.php
// Pest 测试文件:不需要类,直接用 it() 或 test()
it('can create a task', function () {
$user = User::factory()->create();
$response = $this->actingAs($user, 'sanctum')
->postJson('/api/v1/tasks', [
'title'    => '写技术文档',
'status'   => 'pending',
'priority' => 3,
]);
$response->assertStatus(201)
->assertJsonPath('data.title', '写技术文档')
->assertJsonPath('data.status', 'pending');
$this->assertDatabaseHas('tasks', [
'title'   => '写技术文档',
'user_id' => $user->id,
]);
});
// PHPUnit 对比(更繁琐):
class TaskTest extends TestCase
{
public function test_can_create_a_task(): void
{
// 同样的代码,但要套类和方法
}
}

describe() 分组测试

// tests/Feature/TaskTest.php
use App\Models\Task;
use App\Models\User;
describe('Task API', function () {
// beforeEach:每个测试前执行(类似 setUp)
beforeEach(function () {
$this->user = User::factory()->create();
});
describe('listing tasks', function () {
it('returns paginated tasks for the authenticated user', function () {
Task::factory()->count(15)->forUser($this->user)->create();
Task::factory()->count(5)->create();  // 其他用户的任务
$this->actingAs($this->user, 'sanctum')
->getJson('/api/v1/tasks')
->assertOk()
->assertJsonCount(15, 'data')  // 只返回自己的 15 条
->assertJsonStructure([
'data' => [['id', 'title', 'status', 'priority']],
'meta' => ['total', 'per_page', 'current_page'],
]);
});
it('requires authentication', function () {
$this->getJson('/api/v1/tasks')->assertUnauthorized();
});
});
describe('creating tasks', function () {
it('can create a task with valid data', function () {
$this->actingAs($this->user, 'sanctum')
->postJson('/api/v1/tasks', [
'title'    => '新任务',
'status'   => 'pending',
'priority' => 3,
])
->assertCreated()
->assertJsonPath('data.title', '新任务');
});
it('validates required fields', function () {
$this->actingAs($this->user, 'sanctum')
->postJson('/api/v1/tasks', [])
->assertUnprocessable()  // 422
->assertJsonValidationErrors(['title', 'status', 'priority']);
});
});
});

数据驱动测试(Dataset)

// 用数据集测试多种输入场景
it('validates task status', function (string $status, bool $valid) {
$user = User::factory()->create();
$response = $this->actingAs($user, 'sanctum')
->postJson('/api/v1/tasks', [
'title'    => '测试',
'status'   => $status,
'priority' => 3,
]);
if ($valid) {
$response->assertCreated();
} else {
$response->assertUnprocessable()
->assertJsonValidationErrors(['status']);
}
})->with([
'pending is valid'    => ['pending', true],
'in_progress is valid'=> ['in_progress', true],
'done is valid'       => ['done', true],
'invalid status'      => ['flying', false],
'empty status'        => ['', false],
]);

Pest 期望(Expectations)

// Pest 提供了丰富的 expect() API
it('returns correct task structure', function () {
$user = User::factory()->create();
$task = Task::factory()->forUser($user)->create();
$response = $this->actingAs($user, 'sanctum')
->getJson("/api/v1/tasks/{$task->id}")
->assertOk();
// 链式 expect 断言
expect($response->json('data'))
->toBeArray()
->toHaveKeys(['id', 'title', 'status', 'priority', 'created_at'])
->not->toHaveKey('deleted_at')  // 确保不暴露敏感字段
->and($response->json('data.id'))
->toBe($task->id)
->and($response->json('data.title'))
->toBeString()
->not->toBeEmpty();
});
// 常用 expect 方法:
expect($value)
->toBe($expected)           // 全等
->toEqual($expected)        // 宽松相等
->toBeTrue() / ->toBeFalse()
->toBeNull() / ->not->toBeNull()
->toBeString() / ->toBeInt()
->toBeArray() / ->toHaveCount(5)
->toContain($item)
->toStartWith('prefix')
->toMatch('/regex/')
->toBeInstanceOf(User::class)
->toHaveKeys(['key1', 'key2'])
->toBeGreaterThan(0)
->toBeLessThanOrEqual(100);

全局测试配置

// tests/Pest.php(全局配置)
uses(Tests\TestCase::class)->in('Feature');  // Feature 目录使用 TestCase 基类
uses(Tests\TestCase::class)->in('Unit');
// 全局 Trait
uses(Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature');
// 等于在每个测试文件中 use RefreshDatabase
// 全局 Helper
function actingAsUser(array $attributes = []): User
{
$user = User::factory()->create($attributes);
test()->actingAs($user, 'sanctum');
return $user;
}

下一节HTTP 测试与数据库策略——RefreshDatabase vs DatabaseTransactions,以及如何测试认证流程、文件上传、分页的标准姿势。