Laravel 模拟多重依赖

Posted

技术标签:

【中文标题】Laravel 模拟多重依赖【英文标题】:Laravel mock multiple dependency 【发布时间】:2022-01-09 00:01:18 【问题描述】:

我有一个依赖于 BillingService 的 Controller,而 BillingService 又依赖于 UserService。

我需要调用 Controller 方法 getPlans,在这个调用中我需要模拟两个函数:

BillingService 中的 loadPlans UserService 中的 getUsage

这是完整的例子:

class BillingPlanController

    public function __construct(private BillingPlanService $billingPlanService)
    
    

    public function getPlans()
    
        $plans = $this->billingPlanService->getPlans();
        //
    


class BillingPlanService

    public function __construct(private UserService $userService)
    
    

    public function getPlans()
    
        $plans = $this->loadPlans();

        $user = auth()->user();
        $usage = $this->userService->getUsage(user); // DO SOMETHING, NEED TO MOCK .. HOW ?
    

    public function loadPlans()
    
        // DO SOMETHING, NEED TO MOCK .. HOW ?
    

最后,在我的测试中,我只是调用:

getJson(action([BillingPlanController::class, "getPlans"]));

在其他测试中,我能够模拟单个服务,但在这种情况下,我不知道如何编写模拟。

对不起,如果我没有提供任何“尝试”,但我真的不知道我该怎么做。

更新

我尝试使用 partialMockmock,但出现此错误(调用 getUsage 时) - 使用了 partialMock,因为我只是需要模拟一个函数:

Typed property App\Modules\Billing\Services\BillingPlanService::$userService must not be accessed before initialization

$this->mock(UserService::class, function ($mock) 
     $mock->shouldReceive("getUsage")->andReturn([]);
);

$this->partialMock(BillingPlanService::class, function ($mock) 
   $mock->shouldReceive("loadPlans")->andReturn([]);
);

getJson(action([BillingPlanController::class, "getPlans"]));

     

【问题讨论】:

如果你完全模拟而不是部分模拟服务会发生什么? 我在第一个服务上使用了 partialMock,因为 getPlans 是我需要测试的真正功能 - 如果我完全模拟他,功能未定义并且我得到:收到 Mockery_3_App_Modules_Billing_Services_BillingPlanService::getPlans(),但是没有指定期望我正在调用控制器,因为在此调用之后,我也需要测试响应。它是一个集成测试 【参考方案1】:

您在部分模拟中的例外是因为当您模拟 BillingPlanService 时,您不会因为 userService 是模拟而被引入。你可以简单地在模拟上设置它,我认为它应该在你的上下文中工作。

$userServiceMock = $this->mock(UserService::class, function ($mock) 
    $mock->shouldReceive("getUsage")->andReturn([]);
);

$this->partialMock(BillingPlanService::class, function ($userServiceMock) use ($userServiceMock) 
    $mock->set('userService', $userServiceMock);
    $mock->shouldReceive("loadPlans")->andReturn([]);
);

【讨论】:

同样的 :( 使用 $mock->userService = $userServiceMock 给出同样的错误.. 使用您的解决方案(以这种方式有效),我需要将 UserService 标记为公开,但我会避免公开... 我阅读了文档,似乎 set 可以提供帮助,如果没有帮助,我将在周末创建一个本地示例,看看我能做什么。我猜这是属性的组合,它是部分的。我有我编写的示例代码,它在 Mocks 而不是部分设置属性。 对于公共,它按预期工作。但正如我所说,我认为将 DI 标记为 public 是不好的。或者至少我不想要它:) 我继续试验,如果我找到了解决方案,我会写在这里。如果你也可以在 WE 中,我真的很感激。我写的代码你认为是“奇怪的”吗?有我的例子中的 DI 吗? 你的代码是干净的,没有得到私有财产的部分。有一些选项可以将其伪装成公开gist.github.com/JeffreyWay/5287312

以上是关于Laravel 模拟多重依赖的主要内容,如果未能解决你的问题,请参考以下文章

模拟 Laravel 控制器依赖

Laravel 依赖注入:啥时候需要?啥时候可以模拟 Facades?两种方法的优点?

Laravel 8 工厂的多重关系

Laravel 多重计数多重连接

Laravel 自定义多重身份验证

多重连接 laravel 雄辩