Laravel 9中如何Mock带构造函数参数的类?
问题原因与解决方案:Mock带构造参数的Laravel服务类
问题原因
你遇到的核心问题是:当通过App::make(APICaller::class, ['apiToCall' => $api])或makeWith传入构造参数时,Laravel容器会将这些参数视为临时覆盖依赖的指令,此时它会忽略你之前绑定的Mock实例,直接创建一个新的真实APICaller类实例——这就是为什么测试中Mock的预期调用没触发,反而真实类的日志会输出。
而当你移除构造参数后,App::make会直接使用容器中已绑定的Mock实例,测试自然正常。
解决方案
根据你的需求场景,有几种可行的解决方式:
1. 绑定Mock时传入构造参数
如果测试中apiToCall的值是固定的,你可以在创建Mock时直接传入构造参数,这样容器解析时会优先使用这个带参数的Mock:
// 方式一:用Laravel的mock方法 $this->mock(App\Libraries\APICaller::class, ['apiToCall' => '固定测试API'], function (MockInterface $mock) { $mock->shouldReceive('callTheApi')->once(); }); // 方式二:用Mockery手动绑定实例 $this->instance( APICaller::class, Mockery::mock(APICaller::class, ['apiToCall' => '固定测试API'], function (MockInterface $mock) { $mock->shouldReceive('callTheApi')->once(); }) );
2. 改用Setter方法注入动态参数
如果apiToCall需要根据场景动态变化,可以把构造参数改成Setter方法注入,避免在make时传参:
// 修改APICaller类 class APICaller { protected $apiToCall; // 移除带参数的构造函数,或保留空构造 public function __construct() {} public function setApiToCall(string $api): void { $this->apiToCall = $api; } public function callTheApi() { // 业务逻辑 } } // 控制器中调用 $APICaller = App::make(APICaller::class); $APICaller->setApiToCall($api); $APICaller->callTheApi();
此时测试中绑定的Mock会被正常使用,无需处理构造参数。
3. 自定义容器解析逻辑(动态参数场景)
如果必须保留构造函数传参且参数是动态的,可以重写容器的绑定逻辑,让它无论接收什么参数都返回Mock实例:
$this->app->bind(APICaller::class, function ($app, $parameters) { // 根据传入的参数创建Mock $mock = Mockery::mock(APICaller::class, $parameters); // 设置预期调用 $mock->shouldReceive('callTheApi')->once(); return $mock; });
这样不管你在控制器中用make传入什么apiToCall参数,容器都会返回对应的Mock实例,测试预期也会生效。
内容的提问来源于stack exchange,提问作者Orchis
相关产品推荐
相关产品推荐

