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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 20:32:51