Laravel使用Mockery模拟时报错No matching handler,求解决方案
解决Mockery匹配Laravel模型实例的问题
遇到这个错误核心原因是Mockery默认严格匹配对象的内存实例——如果测试里你期望的Message实例和实际传给execute的不是同一个内存对象,就会触发无匹配的异常。下面给你几个实用的解决办法:
1. 自定义属性匹配(最常用)
不用纠结是不是同一个实例,直接检查模型的关键属性(比如id)是否符合预期,用Mockery::on()实现:
// 先定义期望的模型和更新数据 $expectedMessage = Message::factory()->make(['id' => 123]); $expectedUpdateData = ['content' => '新的消息内容']; // 创建Mock对象 $actionMock = Mockery::mock(MessageUpdateAction::class); $actionMock->shouldReceive('execute') ->once() ->with( // 自定义匹配逻辑:检查传入的message的id和期望的一致 Mockery::on(function($message) use ($expectedMessage) { return $message->id === $expectedMessage->id; }), // 直接匹配更新数据 $expectedUpdateData ); // 绑定到容器,让业务逻辑使用这个Mock $this->app->instance(MessageUpdateAction::class, $actionMock); // 执行你的测试逻辑,比如调用控制器方法
这种方式不管业务逻辑里是直接传模型实例还是重新查询后传递,只要id匹配就能通过,适合大部分场景。
2. 确保使用同一个内存实例
如果你确实需要验证是同一个对象,那要保证测试中传递的和业务逻辑里用的是同一个实例:
// 创建一个实际的模型实例(用create存入数据库,避免重新查询生成新实例) $message = Message::factory()->create(); $updateData = ['content' => '更新内容']; $actionMock = Mockery::mock(MessageUpdateAction::class); $actionMock->shouldReceive('execute') ->once() ->with($message, $updateData); $this->app->instance(MessageUpdateAction::class, $actionMock); // 执行业务逻辑时,确保传入的是上面的$message,而不是重新查询的实例 // 比如控制器方法里直接用传入的$message,而不是Message::find($id)
注意:如果业务逻辑里有重新查询模型的操作(比如Message::find($message->id)),会生成新的内存实例,这时候严格匹配就会失败,这种情况还是用第一种方法更靠谱。
3. 匹配类型+数据验证
如果只需要确保传入的是Message类型,同时更新数据正确,可以结合Mockery::type()和自定义匹配:
$actionMock->shouldReceive('execute') ->once() ->with( Mockery::type(Message::class), Mockery::on(function($data) use ($expectedUpdateData) { return $data === $expectedUpdateData; }) );
另外,测试后别忘了清理Mockery,避免内存泄漏:
protected function tearDown(): void { parent::tearDown(); Mockery::close(); }
内容的提问来源于stack exchange,提问作者Bubble Initiative
相关产品推荐
相关产品推荐

