功能测试(Feature Test)用于验证应用的整体行为,从用户角度模拟请求,确保各个组件协同工作。在 Laravel 中,你可以轻松编写 HTTP 测试、数据库测试,并通过模拟(Mocking)外部依赖来隔离测试范围。本章将系统讲解如何编写功能测试,以及如何利用 Laravel 的辅助方法模拟邮件、事件、队列、HTTP 请求等,构建高效、可靠的测试套件。
功能测试类位于 tests/Feature 目录,通常继承自 Tests\TestCase。运行测试:
php artisan test
# 或运行特定测试文件
php artisan test --filter=UserRegistrationTest
<?php
namespace Tests\Feature;
use Tests\TestCase;
class HomepageTest extends TestCase
{
public function test_homepage_returns_ok()
{
$response = $this->get('/');
$response->assertStatus(200);
$response->assertSee('Welcome');
}
}
| 断言方法 | 说明 |
|---|---|
$response->assertStatus(200) | 断言状态码 |
$response->assertOk() | 断言 200 OK |
$response->assertViewIs('welcome') | 断言返回的视图名称 |
$response->assertViewHas('name', $value) | 断言视图数据包含指定键值 |
$response->assertSee('文本') | 断言响应包含文本 |
$response->assertJson(['key' => 'value']) | 断言 JSON 响应包含指定结构 |
$response->assertRedirect('/login') | 断言重定向到指定 URL |
$response->assertSessionHas('success') | 断言 Session 包含指定值 |
$response->assertSessionHasErrors(['email']) | 断言 Session 包含字段错误 |
使用 RefreshDatabase trait 可以在每个测试后重置数据库,避免数据污染。
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserRegistrationTest extends TestCase
{
use RefreshDatabase;
public function test_user_can_register()
{
$response = $this->post('/register', [
'name' => 'John Doe',
'email' => 'john@example.com',
'password' => 'password',
'password_confirmation' => 'password',
]);
$response->assertRedirect('/dashboard');
$this->assertDatabaseHas('users', ['email' => 'john@example.com']);
}
}
工厂用于快速生成测试数据。Laravel 11 默认使用 database/factories 目录,你可以定义工厂并创建模型:
// 创建单个用户
$user = User::factory()->create();
// 创建带关联的用户
$user = User::factory()
->has(Post::factory()->count(3))
->create();
在功能测试中,我们经常需要模拟邮件、事件、队列、HTTP 请求等,避免实际执行外部操作。Laravel 提供了便捷的 fake()
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeMail;
public function test_welcome_email_is_sent()
{
Mail::fake();
$this->post('/register', [...]);
Mail::assertSent(WelcomeMail::class, function ($mail) use ($user) {
return $mail->user->id === $user->id;
});
}
use Illuminate\Support\Facades\Event;
use App\Events\UserRegistered;
Event::fake();
// 执行注册...
Event::assertDispatched(UserRegistered::class);
Event::assertNotDispatched(AnotherEvent::class);
use Illuminate\Support\Facades\Queue;
use App\Jobs\SendWelcomeEmail;
Queue::fake();
// 执行注册...
Queue::assertPushed(SendWelcomeEmail::class, function ($job) use ($user) {
return $job->user->id === $user->id;
});
use Illuminate\Support\Facades\Http;
Http::fake([
'api.github.com/*' => Http::response(['name' => 'John'], 200),
]);
$response = $this->get('/github-user');
// 断言响应包含 'John'
use Illuminate\Support\Facades\Notification;
use App\Notifications\UserRegisteredNotification;
Notification::fake();
// 执行注册...
Notification::assertSentTo($user, UserRegisteredNotification::class);
use Illuminate\Support\Facades\Storage;
Storage::fake('public');
// 执行文件上传...
Storage::disk('public')->assertExists('uploads/file.jpg');
使用 actingAs() 方法模拟已登录用户:
$user = User::factory()->create();
$this->actingAs($user)
->get('/dashboard')
->assertOk();
如果需要模拟特定 guard 的用户,可以传递第二个参数:
$this->actingAs($user, 'api');
Laravel 提供了方便的方法测试 JSON API,并验证 JSON 结构。
public function test_api_returns_users()
{
$user = User::factory()->create();
$response = $this->getJson('/api/users/' . $user->id);
$response->assertStatus(200)
->assertJson([
'id' => $user->id,
'name' => $user->name,
])
->assertJsonStructure([
'id', 'name', 'email', 'created_at'
]);
}
public function test_registration_requires_valid_email()
{
$response = $this->post('/register', [
'name' => 'John',
'email' => 'invalid-email',
'password' => 'password',
'password_confirmation' => 'password',
]);
$response->assertSessionHasErrors(['email']);
}
public function test_user_can_upload_avatar()
{
Storage::fake('public');
$file = UploadedFile::fake()->image('avatar.jpg');
$this->actingAs($user)
->post('/avatar', ['avatar' => $file])
->assertOk();
Storage::disk('public')->assertExists('avatars/' . $file->hashName());
}
RefreshDatabase 或 DatabaseMigrations 确保数据库干净。test_user_cannot_register_with_invalid_email。php artisan test --coverage 可查看代码覆盖率(需要安装 Xdebug 或 Pcov)。
Laravel 的功能测试与模拟工具让编写高质量测试变得简单而愉快。通过模拟外部依赖,你可以快速验证业务逻辑,确保应用在真实环境中正确运行。养成编写测试的习惯,将极大提升代码的可维护性和自信心。