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

使用Mockery模拟User静态方法时触发BadMethodCallException异常求助

排查Laravel中Mock静态模型方法触发BadMethodCallException的问题

问题原因分析

你遇到的BadMethodCallException本质是Laravel Eloquent的静态方法转发机制导致的:当你调用User::firstName()这类模型上不存在的静态方法时,Eloquent会自动创建一个Illuminate\Database\Query\Builder实例,并把方法调用转发给它。而你的Mock方式没有正确拦截这个静态调用,导致实际请求落到了查询构建器上——它自然没有firstName()方法,所以报错。

再看你的代码,问题出在Mock的逻辑上:你创建了一个User实例的Mock,然后用Test::double把firstName映射到这个实例,但这不是模拟静态方法的正确姿势。Test::double在这里并没有帮你拦截静态调用,所以User::firstName()还是走了Eloquent的默认转发逻辑。

解决办法

要正确模拟静态方法调用,你需要用Mockery的别名Mock(alias:前缀)来拦截整个User类的静态方法调用,然后指定它的返回值(如果需要返回实例,还要预先模拟实例的方法)。

方案1:直接模拟静态方法返回带实例方法的对象

// 创建别名Mock,拦截User类的静态方法调用
Mockery::mock('alias:User')
    ->shouldReceive('firstName')
    ->with('user-51e06817-c03c822b')
    ->andReturn((object) [
        'firstName' => function($id) {
            return 'Luis';
        }
    ]);

$userOP = User::firstName('user-51e06817-c03c822b');
$this->info($userOP->firstName('1'));

方案2:先模拟实例方法,再让静态方法返回该实例

如果firstName静态方法原本就应该返回User实例,这种方式更贴合真实逻辑:

// 先模拟User实例的firstName方法
$userInstance = Mockery::mock(User::class);
$userInstance->shouldReceive('firstName')
    ->with('1')
    ->andReturn('Luis');

// 再模拟User类的静态firstName方法,返回上面的实例
Mockery::mock('alias:User')
    ->shouldReceive('firstName')
    ->with('user-51e06817-c03c822b')
    ->andReturn($userInstance);

$userOP = User::firstName('user-51e06817-c03c822b');
$this->info($userOP->firstName('1'));

额外注意事项

  • 如果你的测试是Laravel框架内的单元测试,建议使用User::class替代字符串'User',避免命名空间问题。
  • 测试完成后记得调用Mockery::close()清理Mock实例,防止影响其他测试用例。

内容的提问来源于stack exchange,提问作者Luis Osuna

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:35:43