如何使用Mockery正确mock Laravel模型的save()方法 避免触发数据库请求
Mock save方法未生效问题解决方案
根本原因
你当前写法的核心问题是:JobAccessor::find()返回的是你手动new出来的真实Job模型实例,和你在setup函数中创建并绑定到容器的$this->job mock对象不是同一个实例。你给$this->job设置的save方法调用预期,不会作用到真实实例上,因此$job->save()调用时依然会执行原生数据库逻辑,抛出数据库驱动异常。
另外注意:你在容器中绑定的App\Models\Job mock,仅对通过容器make()方式创建的Job实例生效,手动new出来的Job实例不受容器绑定约束。
解决方法
方案1:让find方法返回带save预期的mock实例
直接构造mock的Job对象,绑定save调用预期后,作为find方法的返回值即可:
// 测试函数修改后代码 // 构造partial mock,保留模型原有属性/方法逻辑,仅mock需要屏蔽的save方法 $mockJob = Mockery::mock(Job::class)->makePartial(); $mockJob->id = 22; $mockJob->user_id = 123; // 绑定save方法的调用预期 $mockJob->shouldReceive('save')->once()->andReturnTrue(); $this->jobAccessor->shouldReceive('find') ->with($jobId) ->andReturns($mockJob);
方案2:对已有的真实Job实例做部分mock
如果你需要保留new Job()初始化的属性逻辑,可以对已创建的真实实例做部分mock,只重写save方法:
// 测试函数修改后代码 $job = new Job([ 'id' => 22, 'user_id' => 123 ]); // 对现有实例做partial mock,仅覆盖save方法 $mockJob = Mockery::mock($job)->makePartial(); $mockJob->shouldReceive('save')->once()->andReturnTrue(); $this->jobAccessor->shouldReceive('find') ->with($jobId) ->andReturns($mockJob);
注意事项
- 可以根据测试场景调整
save方法的返回值,比如andReturnFalse()模拟保存失败的分支逻辑 - 如果不需要保留模型原生逻辑,可去掉
makePartial(),创建纯mock对象
内容的提问来源于stack exchange,提问作者Luke
相关产品推荐
相关产品推荐

