功能测试必须走真实http请求链路,需用refreshdatabase确保数据库干净、actingas建立完整认证会话、覆盖重定向/视图/数据库变更等端到端行为,不可跳过关键中间件。

功能测试必须走真实 HTTP 请求链路,不能只测逻辑;不加 RefreshDatabase 或跳过中间件,很容易因数据残留或 CSRF 拦截导致断言失败。
测试类必须用 RefreshDatabase 并配好 SQLite 内存库
每次测试前数据库要干净,否则上一个测试插入的用户可能让当前测试的「注册邮箱唯一性」断言意外通过。Laravel 默认的 RefreshDatabase Trait 会自动回滚迁移,但前提是数据库配置得对。
- 检查
phpunit.xml中是否设置了DB_CONNECTION=sqlite和DB_DATABASE=:memory: - 不要在测试里手动调用
Artisan::call('migrate')——RefreshDatabase已处理 - 若用 MySQL 测试,需确保
DB_DATABASE是独立测试库(如testing_db),且每次测试前被清空
登录验证必须用 actingAs 而非 post('/login')
直接 POST 登录表单只能测控制器逻辑,但无法建立完整的认证会话状态(比如 session、guard、remember token)。很多中间件(如 auth:sanctum)和视图中的 @auth 判断会因此失效。
- API 测试:用
$this->actingAs($user, 'sanctum')或'passport' - Web 表单流程:仍可用
$this->post('/login', [...]),但之后必须加$this->assertAuthenticated()显式确认 - 别漏掉重定向后的页面断言,比如
$response->assertRedirect('/dashboard')后,再跟一次$this->get('/dashboard')->assertSee(...)
跳过中间件不是默认选项,而是明确选择
$this->withoutMiddleware() 看起来省事,但它会让 CSRF、rate limiting、trust proxies 等关键防护失效——这些恰恰是功能测试该覆盖的部分。只有当你确认问题与中间件无关(比如专注测控制器返回值格式),才临时禁用。
- 全局跳过:
$this->withoutMiddleware()放在setUp()或测试方法开头 - 局部跳过:用
$this->withoutMiddleware([VerifyCsrfToken::class])只关指定中间件 - 注意:跳过中间件后,
$this->withSession([...])手动设 session 可能不生效,因为 session 中间件也被跳过了
断言响应时,别只看 status 和 JSON 结构
功能测试的核心价值在于端到端验证,光有 assertStatus(200) 和 assertJson(['id' => 1]) 不够。数据库变更、会话状态、重定向路径、甚至视图中渲染的文本,都得覆盖。
- 数据库写入:用
$this->assertDatabaseHas('users', ['email' => 'test@example.com']) - 会话数据:用
$this->assertSessionHas('success', 'User created') - 重定向目标:用
assertRedirect('/profile'),而不是只测get('/profile') - Blade 渲染文本:用
$response->assertSee('Welcome back, John'),它比 JSON 断言更贴近真实用户视角
最常被忽略的是「中间件顺序」和「guard 配置」——比如你用了 auth:api 但测试时 actingAs 指定了 'web',或者路由组里套了 throttle:api 却没在测试中模拟限流头。这些细节不显眼,但会让测试在 CI 上随机失败。











