Laravel 单元测试入门指南
在 Laravel 中用 PHPUnit 写测试:创建与运行测试、HTTP 响应断言、数据库测试与假数据、Dusk 浏览器测试、Mock 外部 API,以及用 GitHub Actions 搭建 CI 测试流程。
本文依据官方文档整理,未执行运行验证或性能基准。代码片段展示局部用法,业务函数、数据和环境需按项目补齐;版本与配置以所引文档为准。
直接回答:Laravel 开箱即集成了 PHPUnit,项目创建后即可直接编写两类测试——单元测试聚焦类中的单个方法,功能测试验证多个类协作完成的完整特性;配合框架自带的 HTTP 断言、数据库工具和 Mock 门面,可以覆盖从模型到接口的全链路。
Laravel 测试的两个层次
| 类型 | 关注点 | 典型位置 |
|---|---|---|
| 单元测试(Unit) | 单个方法的逻辑正确性 | tests/Unit |
| 功能测试(Feature) | 多个组件协作的特性行为 | tests/Feature |
Laravel 新建项目自带这两个目录和示例用例,php artisan test(或 ./vendor/bin/phpunit)即可运行。
创建测试
php artisan make:test OrderServiceTest --unit # 单元测试
php artisan make:test OrderApiTest # 功能测试(默认)
一条典型的单元测试:
public function test_order_total_is_sum_of_items(): void
{
$service = new OrderService();
$this->assertEquals(300, $service->total([100, 200]));
}
PHPUnit 提供了大量断言:assertTrue、assertCount、assertInstanceOf;异常预期常用 $this->expectException(...) 等,绝大多数场景不用手写 if-else。
测试 HTTP 响应
功能测试里最常用的是 HTTP 断言链:
public function test_create_order(): void
{
$response = $this->postJson('/api/orders', ['sku' => 'A1', 'qty' => 2]);
$response->assertStatus(201)
->assertJsonPath('data.sku', 'A1');
}
getJson、postJson、putJson、deleteJson 覆盖常见动词,断言可以连续检查状态码、JSON 结构、Header 和 Session。
数据库测试与假数据
功能测试类继承 Tests\TestCase 并加 RefreshDatabase,框架按数据库状态迁移并通常用事务隔离。务必使用专用测试库,不能指向生产;普通 PHPUnit Unit 测试并未引导 Laravel。SQLite 与生产数据库方言不同,不能替代同引擎集成测试:
use Illuminate\Foundation\Testing\RefreshDatabase;
class OrderTest extends TestCase
{
use RefreshDatabase;
public function test_order_is_stored(): void
{
Order::factory()->create(['total' => 99]);
$this->assertDatabaseHas('orders', ['total' => 99]);
}
}
factory() 结合 Faker 能批量生成逼真假数据,避免手写一堆 INSERT。
浏览器测试:Laravel Dusk
需要验证真实浏览器行为(JS 交互、页面跳转)时用 Dusk:它驱动真实 Chrome 执行点击、填表、截图断言。适合覆盖少量关键用户路径,不建议大规模铺开——浏览器测试慢且脆。
Mock 外部 API
单元/常规功能测试可 Mock 第三方;另以经授权的沙箱集成测试验证真实契约:
Http::fake([
'api.payment.com/*' => Http::response(['status' => 'ok'], 200),
]);
Http::fake 只拦截 Laravel HTTP Client 发出的请求;可配 Http::preventStrayRequests 防止未匹配请求意外出网,还能用 Http::assertSent 断言请求体是否正确。Mail::fake()、Queue::fake()、Event::fake() 同理。
接入 GitHub Actions
- name: Run tests
run: php artisan test
把测试挂进 push/PR 触发的 workflow,配合 PHP 版本矩阵,就得到了最基本的 CI 门禁:测试不过,代码不合。
常见问题(FAQ)
Q:单元测试和功能测试的比例怎么把握?
A:以金字塔为参考:大量单元测试打底,适量功能测试覆盖关键流程,极少量 Dusk 覆盖核心页面。功能测试占比过高会拖慢套件且定位困难。
Q:测试连真实数据库可以吗?
A:可以但要隔离:专用测试库 + RefreshDatabase。数据库特性测试优先使用与生产同引擎的隔离实例;SQLite 只用于明确不依赖方言差异的路径。
Q:Dusk 测试在 CI 里跑不动怎么办?
A:CI 需要无头 Chrome 和 chromedriver,GitHub Actions 有现成方案。若维护成本过高,把 Dusk 缩到 3~5 条最关键路径即可。
官方参考
资料核对日期:2026-09-29。