Laravel单元测试mock外部IntegrationApi服务不生效如何解决?
问题原因
- Laravel提供的
$this->mock()方法只会将mock对象绑定到Laravel的服务容器中,不会替换代码中直接用new关键字实例化的类对象。当前IntegrationService::create方法里硬编码了$api = new IntegrationApi();,这个实例化过程完全不走服务容器,所以mock规则不会作用到这个实例上,这是核心问题。 - 测试代码中mock的参数校验写的是
withArgs(['apiKey']),但实际调用时传入的api_key是'123',参数不匹配也会导致mock不生效,属于次要问题。
正确解决方法
方案1:依赖注入解耦(推荐,符合Laravel最佳实践)
首先修改IntegrationService,通过构造函数注入IntegrationApi实例,不要在方法内部手动实例化:
class IntegrationService { // 通过构造函数注入依赖 public function __construct(protected IntegrationApi $api) {} public function create(array $params) { if (!$this->api->checkCredentials($params['api_key'])) { throw new \Exception('Invalid credentials'); } // storing to DB return 'ok'; // just for example } }
修改测试代码,先注册mock再从容器解析IntegrationService,保证依赖自动注入mock对象:
class TestIntegrationService extends TestCase { public function test_create() { // 先注册mock,参数要和实际传入的'123'匹配 $this->mock(IntegrationApi::class, function (MockInterface $mock) { $mock->shouldReceive('checkCredentials')->withArgs(['123'])->once()->andReturnTrue(); }); // 从服务容器解析IntegrationService,会自动注入mock好的IntegrationApi $service = app(IntegrationService::class); $res = $service->create(['api_key' => '123']); $this->assertEquals('ok', $res); } }
方案2:容器实例化(无需修改构造函数)
如果不想调整IntegrationService的构造函数,可以将方法内部的实例化逻辑改为从服务容器获取对象:
class IntegrationService { public function create(array $params) { // 从服务容器解析实例,不要直接new $api = app(IntegrationApi::class); if (!$api->checkCredentials($params['api_key'])) { throw new \Exception('Invalid credentials'); } // storing to DB return 'ok'; // just for example } }
测试代码只需把mock的参数改为匹配的'123'即可正常生效。
内容的提问来源于stack exchange,提问作者user137
相关产品推荐
相关产品推荐

