最直接有效的方式是用 withoutMiddleware() 方法临时禁用中间件;它只能在 setUp() 或测试方法中调用,作用于当前请求,需传完整类名而非别名,可与 actingAs() 联用,API 路由中若显式添加中间件仍需跳过。

测试中跳过中间件最直接有效的方式,是用 withoutMiddleware() 方法临时禁用全局或分组中间件,而不是靠修改配置或重写测试环境。
测试里怎么调用 withoutMiddleware()
它只能在测试类的 setUp() 或具体测试方法里调用,作用范围仅限当前请求。Laravel 会把中间件注册逻辑“冻结”在该请求生命周期内,后续请求不受影响。
- 禁用全部中间件:
$this->withoutMiddleware(); - 只禁用某一个:
$this->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]); - 禁用多个,传数组:
$this->withoutMiddleware([\App\Http\Middleware\Authenticate::class, \App\Http\Middleware\TrimStrings::class]);
注意:不能传中间件别名(如 'auth'),必须传完整类名;否则 Laravel 找不到对应类, silently 忽略掉,导致你以为跳过了,其实没生效。
withoutMiddleware() 和 actingAs() 能一起用吗
能,而且推荐一起用——withoutMiddleware() 解除中间件拦截,actingAs() 模拟登录态,两者职责分明。比如测一个需要登录但又不想被 Authenticate 中间件挡掉的 API:
$response = $this->withoutMiddleware([\App\Http\Middleware\Authenticate::class])
->actingAs($user)
->getJson('/api/v1/profile');
常见错误是只用 actingAs() 却不关中间件,结果请求在到达控制器前就被重定向或 401;或者反过来,只关中间件却没模拟用户,导致控制器里 auth()->user() 为 null,引发空指针异常。
为什么不用 APP_ENV=testing 配置来跳过中间件
因为中间件注册发生在应用启动阶段,而 APP_ENV 只影响日志、调试等行为,并不会自动删掉 $middleware 数组里的类。你得手动在 Kernel.php 里加条件判断,比如:
// 不推荐:污染生产配置
if (app()->environment('testing')) {
$this->middleware = array_filter($this->middleware, function ($m) {
return ! in_array($m, [\App\Http\Middleware\Authenticate::class]);
});
}
这会让测试逻辑侵入框架核心配置,且无法按测试用例粒度控制,容易误伤其他测试。相比之下,withoutMiddleware() 是声明式、局部、可预测的。
API 路由测试时 withoutMiddleware() 还需要吗
大多数情况下不需要——routes/api.php 默认不走 web 中间件组,也就没有 VerifyCsrfToken、EncryptCookies 等。但如果你在 api 路由里显式加了 auth:sanctum 或自定义中间件,那依然要跳过它们才能专注测业务逻辑。
比如你写了 Route::middleware(['auth:sanctum', 'throttle:api'])->get('/posts', ...),测试时就得:$this->withoutMiddleware([\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class]),否则 actingAs() 不起作用。
真正容易被忽略的是中间件执行顺序:如果某个中间件依赖前置中间件生成的 request 属性(比如 $request->user() 或 $request->route()),跳过它可能导致后续中间件报错——这种时候不是跳过,而是该换用 swap() 替换中间件实现,或者干脆用真实中间件跑集成测试。


















