Laravel测试中如何正确模拟Eloquent模型的getRoleNames方法?
问题诊断与解决方案
错误原因
你当前的测试逻辑存在核心问题:
actingAs($user)已经将真实的User实例注入到Laravel的认证系统中,后续auth()->user()返回的是这个真实实例,而非你绑定到服务容器的Mock对象。- Laravel的认证系统不会从服务容器中解析用户实例,而是从会话/请求上下文获取,所以直接替换
User::class的容器绑定无法影响auth()->user()的返回值。
正确的模拟方案
方案一:局部Mock已认证用户的目标方法
直接对actingAs使用的用户实例进行部分Mock,只替换getRoleNames方法:
test('roles()', function () { // 创建真实用户实例 $user = $this->createUser(); // 对实例做部分Mock,保留其他原有逻辑,仅替换getRoleNames $mockedUser = Mockery::mock($user)->makePartial(); $mockedUser->shouldReceive('getRoleNames') ->once() ->andReturn(new Collection(['role1', 'role2'])); // 使用Mock后的用户设置认证状态 $this->actingAs($mockedUser); $controller = new LoginController(); $roles = $controller->roles(); // 断言结果符合预期 $this->assertEquals(['role1', 'role2'], $roles->toArray()); });
方案二:替换认证系统的用户解析器
如果需要全局让auth()->user()返回Mock对象,可以替换认证系统的用户解析逻辑:
test('roles()', function () { // 创建完整的User Mock对象 $userMock = Mockery::mock(User::class); $userMock->shouldReceive('getRoleNames') ->once() ->andReturn(new Collection(['role1', 'role2'])); // 替换Auth的用户解析器,强制返回Mock对象 $this->app['auth']->setUserResolver(function () use ($userMock) { return $userMock; }); $controller = new LoginController(); $roles = $controller->roles(); $this->assertEquals(['role1', 'role2'], $roles->toArray()); });
关键注意事项
makePartial()的作用:该方法会保留原对象的所有属性和未被Mock的方法,仅替换你指定的方法,非常适合这种需要复用真实用户属性、仅Mock特定 trait 方法的场景。- Mock清理:Laravel的测试框架会自动在测试结束后清理Mockery实例,无需手动调用
Mockery::close()。
内容的提问来源于stack exchange,提问作者PHP Pharaoh
相关产品推荐
相关产品推荐

