Laravel 11升级后测试失效主因是中间件解析、路由安全、响应机制变更;需检查SQLite内存库配置、RefreshDatabase加载、TestCase基类完整性,并改用actingAs($user, 'sanctum')、withSession()、assertStatus()、assertJson()等新契约。

从 Laravel 9 升级到 11 后,原有功能测试用例可能因中间件解析路径变更、路由类型安全增强、响应构造机制重构而失效——比如 @authenticated 标签在文档中消失、$this->actingAs() 模拟登录后仍返回 401、或 assertJsonPath() 断言突然报错“Property not found”,这些都不是代码逻辑问题,而是框架底层测试契约已变。
确认测试环境兼容性
第一步:检查 phpunit.xml 中数据库配置是否仍指向 SQLite 内存库。【DB_CONNECTION 必须为 sqlite,DB_DATABASE 必须为 :memory:】 若误设为 mysql 或 pgsql,所有 RefreshDatabase 行为将操作真实开发库,导致数据污染甚至删除。
第二步:运行 php artisan test --dry-run,观察输出是否包含 Illuminate\Foundation\Testing\RefreshDatabase Trait 的加载提示。若无提示,说明测试基类未正确继承 Tests\TestCase,需打开每个 *Test.php 文件,确认 class XxxTest extends TestCase 声明存在且未被注释。
第三步:执行 php artisan make:test DummyTest --feature 并删掉生成文件中全部内容,仅保留最简结构:public function test_true_is_true() { $this->assertTrue(true); }。运行 php artisan test --filter=DummyTest,若报错 Class 'Tests\TestCase' not found,说明 tests/TestCase.php 被意外删除或命名错误,必须从 Laravel 11 官方仓库重新复制该文件。
重写认证相关功能测试
方法一:替换 Auth::attempt() 模拟逻辑为 actingAs() + Sanctum 显式绑定
旧写法(Laravel 9):$response = $this->post('/login', ['email' => $user->email, 'password' => 'password']); → 在 Laravel 11 中会跳过 Session 认证中间件,导致后续请求始终未登录。
新写法(Laravel 11):$user = User::factory()->create(); $this->actingAs($user, 'sanctum')->get('/dashboard');。这一步必须显式指定 guard 名称,否则 Laravel 11 默认使用 web guard,但 actingAs() 在无中间件上下文时不会自动写入 session。
方法二:对 Web 路由测试,改用 withSession() 注入认证态
$user = User::factory()->create(); $this->withSession(['login_id' => $user->id])->get('/dashboard');。注意:Laravel 11 的 Session 驱动默认为 array,不支持持久化,因此不能依赖 session()->put() 后再发起请求,必须在请求前一次性注入完整 session 数组。
修复中间件元数据断言失效
第一步:打开 app/Http/Kernel.php,删除 $middlewareGroups 和 $middleware 属性的全部定义——Laravel 11 已弃用该文件中的中间件声明,所有中间件注册必须移至 bootstrap/app.php 的闭包中。
第二步:在 config/apidoc.php(如使用 Scribe)中设置 'use_kernel_for_middleware' => false,否则文档生成器仍将尝试反射空 Kernel 文件,导致 @middleware 注释无法关联到实际中间件链。
第三步:验证中间件是否生效,运行 php artisan apidoc:generate --dry-run,检查输出中是否列出 auth:sanctum 或 throttle:api 等预期中间件。若未出现,说明 bootstrap/app.php 中未正确调用 $middleware->api(...) 或 $middleware->web(...) 方法。
第四步:在功能测试中,不再依赖 assertRedirect() 隐式判断中间件拦截,改为显式断言状态码:$this->get('/api/user')->assertStatus(401);。因为 Laravel 11 的中间件跳转逻辑已从重定向改为直接返回 JSON 错误响应。
更新响应断言写法
旧版 assertSee('Welcome') 在 Laravel 11 中对 API 路由失效——它只作用于 HTML 响应体,而 Laravel 11 默认将 api 中间件组的响应强制设为 JSON 格式,即使控制器返回字符串也会被包装成 {"message":"Welcome"}。
正确做法是:对 API 测试统一使用 assertJson() 或 assertJsonPath()。例如 $this->get('/api/status')->assertJson(['status' => 'ok']);。若控制器返回纯文本,需在路由定义中显式添加 ->withoutMiddleware() 或在测试中加 ->withHeaders(['Accept' => 'text/plain']) 才能触发原始响应。
关键点:【Laravel 11 的 ResponseFactory 强制约束响应结构,任何未通过 ResponseFactory 构造的返回值(如直接 return 'ok')将被自动封装为 JSON,且无法用 assertSee 断言原始字符串】。
迁移 RefreshDatabase 使用方式
① 删除所有测试类中手动调用的 Artisan::call('migrate:fresh') —— Laravel 11 的 RefreshDatabase Trait 已优化为在 setUpBeforeClass() 阶段一次性重建迁移,不再每次测试方法执行前重复运行,性能提升 3 倍以上。
② 若测试中使用了 DatabaseMigrations Trait,必须替换为 RefreshDatabase。因为 Laravel 11 移除了 DatabaseMigrations 对 sqlite:memory: 的兼容层,继续使用会导致 SQLSTATE[HY000]: General error: 1 no such table 错误。
③ 对含外键约束的表,无需额外处理。Laravel 11 的 RefreshDatabase 默认启用 foreign_key_constraints: true,只要迁移文件中正确定义了 $table->foreignId('user_id')->constrained();,truncate 操作将自动按依赖顺序清空表。


















