You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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());
});

关键注意事项

  1. makePartial()的作用:该方法会保留原对象的所有属性和未被Mock的方法,仅替换你指定的方法,非常适合这种需要复用真实用户属性、仅Mock特定 trait 方法的场景。
  2. Mock清理:Laravel的测试框架会自动在测试结束后清理Mockery实例,无需手动调用Mockery::close()。

内容的提问来源于stack exchange,提问作者PHP Pharaoh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 04:25:17