【问题标题】:Laravel mock multiple dependencyLaravel 模拟多重依赖
【发布时间】: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(),但是没有指定期望我正在调用控制器,因为在此调用之后,我也需要测试响应。它是一个集成测试

标签: laravel mocking phpunit php-pest


【解决方案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
猜你喜欢
  • 2019-12-25
  • 2015-10-21
  • 1970-01-01
  • 1970-01-01
  • 2020-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多