laravel-testing.md 4.7 KB

# Laravel 测试专项参考

本文件是 test-generation 的 Laravel/PHPUnit 补充参考,不作为独立子技能计数。命中 Laravel、PHPUnit、Pest、Feature Test、Factory、Queue fake、HTTP fake、Sanctum 等信号时按需加载。

测试分层

  • Feature Test:覆盖 HTTP、路由、中间件、授权、校验、数据库副作用,是 Laravel 业务功能的默认优先级。
  • Unit Test:覆盖纯服务、Action、值对象、规则类,不依赖 HTTP,尽量少碰数据库。
  • Integration Test:覆盖队列、外部服务适配器、缓存、文件系统等跨边界行为。
  • Regression Test:每个已修复缺陷至少补一条失败先行或可复现用例。

基础规则

  1. 一个测试只验证一个主要行为,命名使用 test_ 前缀或清晰的 it ... 描述。
  2. 同时断言响应和副作用:HTTP status、JSON 结构、数据库状态、队列/事件/通知是否派发。
  3. 所有测试数据优先使用 Factory,禁止在业务测试里散落 raw DB::table()->insert()。
  4. Fake 必须在 action 前设置:先 Queue::fake(),再执行请求,最后断言。
  5. 外部 HTTP、邮件、通知、队列、事件、Storage 默认 fake,除非当前测试明确验证真实集成。
  6. 涉及权限时同时测允许路径和拒绝路径。

数据库策略

  • RefreshDatabase:默认选择,保证 migration 与测试数据库一致。
  • DatabaseTransactions:更快,但不能验证 migration;仅在项目明确适配时使用。
  • DatabaseMigrations:每个测试运行/回滚 migration,成本高,只在需要验证迁移行为时使用。
  • 数据库断言使用 assertDatabaseHas()、assertDatabaseMissing();金额 DECIMAL 按字符串断言,避免 float 精度问题。

Feature Test 模板

public function test_user_can_update_own_profile(): void
{
    Queue::fake();

    $user = User::factory()->create();

    $response = $this
        ->actingAs($user)
        ->patchJson('/api/profile', [
            'name' => 'Ada Lovelace',
        ]);

    $response
        ->assertOk()
        ->assertJsonPath('success', true)
        ->assertJsonPath('data.name', 'Ada Lovelace');

    $this->assertDatabaseHas('users', [
        'id' => $user->id,
        'name' => 'Ada Lovelace',
    ]);

    Queue::assertNothingPushed();
}

校验与授权测试

  • 表单校验:断言 422、字段错误、不会产生数据库副作用。
  • 授权拒绝:断言 403 或项目统一错误 envelope。
  • 认证缺失:断言 401,并确认不会泄露内部错误。
  • API Resource:断言字段存在、敏感字段不存在、关系字段只在 whenLoaded() 时出现。

Factory 模式

  • 每个核心模型都应有 Factory。
  • 状态使用 state():如 active()、suspended()、verified()。
  • 关系通过 Factory 组合创建,不在测试主体里手写大量依赖数据。
  • 需要副作用的测试可使用 afterCreating(),但要避免隐藏过多业务前置条件。
  • 批量场景使用 sequence 覆盖不同状态和边界值。

Fake 与 Mock

| 工具 | 用途 | 断言 | | - | - | - | | Queue::fake() | 队列任务 | assertPushed()、assertNothingPushed() | | Event::fake() | 领域事件 | assertDispatched() | | Notification::fake() | 通知 | assertSentTo() | | Mail::fake() | 邮件 | assertSent()、assertQueued() | | Storage::fake() | 上传/文件写入 | assertExists()、assertMissing() | | Http::fake() | 外部 API | assertSent()、模拟超时/错误 | | Bus::fake() | chain/batch | assertChained()、assertBatched() |

服务类 mock 只用于隔离外部边界或昂贵依赖;核心业务规则优先真实执行,避免测试只验证 mock 调用。

N+1 与性能回归

  • 对列表接口记录查询数量基线,新增关系字段时必须复测。
  • 使用 with()、withCount()、withExists() 后断言响应字段和查询数量。
  • 性能断言不替代功能断言;如需量化性能,协同 performance-benchmark。

长任务与队列测试

  • Init-Step-Poll 接口需覆盖:初始化成功、重复初始化幂等、单步成功、单步失败、轮询完成、轮询失败。
  • Job 需覆盖:成功路径、异常重试、幂等重复执行、failed() 补偿。
  • 批处理需覆盖:then、catch、finally 的状态变更。

运行命令

php artisan test
./vendor/bin/phpunit
./vendor/bin/phpunit --filter test_user_can_update_own_profile
./vendor/bin/phpstan analyse --level=8

交付证据

交付 Laravel 测试相关任务时,至少提供:

  • 新增/修改的测试文件路径。
  • 运行命令。
  • 通过/失败结果。
  • 如失败,给出阻塞原因和下一步所需输入。