Laravel测试中模拟Policy:验证方法调用时机与参数
解决Laravel单元测试中验证Policy方法调用的问题
我明白你在Laravel单元测试里验证edit Policy是否被正确调用时遇到的麻烦——直接Mock Policy类会因为框架已经提前注册了对应实例而被忽略,尝试模拟Gate Facade也没达到预期效果。其实核心问题在于:Laravel的$this->authorize()是通过Gate服务来关联模型和Policy的,所以我们需要通过Gate来替换掉默认的Policy,让Mock实例生效。
正确的测试步骤与代码示例
下面是完整的测试代码,能验证EntryPolicy的edit方法是否在控制器调用$this->authorize('edit', $entry)时被触发,同时验证参数是否正确:
public function test_controller_edit_calls_entry_policy_edit_method() { // 1. 准备测试所需的请求对象(根据你的路由方法调整) $request = \Illuminate\Http\Request::create('/entries/1/edit', 'GET'); // 2. 创建测试用的Entry模型实例(可以用Factory生成) $entry = \App\Models\Entry::factory()->make(); // 3. Mock EntryPolicy类,设置预期的调用规则 $mockPolicy = \Mockery::mock(\App\Policies\EntryPolicy::class); // 预期edit方法被调用1次,参数为当前登录用户和Entry实例,返回true让授权通过 $mockPolicy->shouldReceive('edit') ->once() ->with(\Illuminate\Support\Facades\Auth::user(), $entry) ->andReturn(true); // 4. 关键步骤:通过Gate将Mock的Policy绑定到Entry模型 \Illuminate\Support\Facades\Gate::policy(\App\Models\Entry::class, fn () => $mockPolicy); // 5. 实例化控制器 $controller = new \App\Http\Controllers\EntryController(); // 6. 调用控制器的edit方法 $response = $controller->edit($request, $entry); // 7. 可选:断言响应状态(比如授权通过后返回200) $this->assertEquals(200, $response->getStatusCode()); // Mockery会自动验证预期的调用是否发生,测试结束后关闭Mock \Mockery::close(); }
为什么之前的方法无效?
你之前尝试直接Mock Policy类然后实例化控制器的方式行不通,原因是:
- Laravel在应用启动时,已经通过
AuthServiceProvider里的$policies数组将模型和Policy绑定到Gate服务中了。 - 控制器调用
$this->authorize()时,会从Gate服务中获取已经注册的Policy实例,而不是你手动创建的Mock实例。 - 所以必须通过
Gate::policy()方法来替换掉默认的Policy绑定,让Gate使用你的Mock实例。
模拟未登录场景的测试
如果需要测试未登录时授权失败的情况,可以这样写:
public function test_edit_throws_authentication_exception_when_not_logged_in() { // 模拟未登录状态:让Auth返回null用户 \Illuminate\Support\Facades\Auth::shouldReceive('user')->andReturnNull(); $request = \Illuminate\Http\Request::create('/entries/1/edit', 'GET'); $entry = \App\Models\Entry::factory()->make(); $controller = new \App\Http\Controllers\EntryController(); // 预期抛出AuthenticationException $this->expectException(\Illuminate\Auth\AuthenticationException::class); // 调用控制器方法,触发授权逻辑 $controller->edit($request, $entry); }
这样就能准确验证不同场景下的授权行为了。
内容的提问来源于stack exchange,提问作者nichtmonti
相关产品推荐
相关产品推荐

