Laravel mock multiple dependency

903 Views Asked by At

I have a Controller that has a dependency with BillingService, and BillingService has another dependency on UserService.

I need to call the Controller method getPlans and in this call I need to mock two functions:

  • loadPlans that is inside BillingService
  • getUsage that is in UserService

This is the full example:

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 ?
    }
}

At the end, in my test i simply call:

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

In other tests, I'm able to mock a single Service, but in this scenario, I don't know how to write the mocks.

Sorry if I don't provide any "tries", but I really don't know how I can do that.

UPDATE

I tried to use partialMock and mock, but I get this error (when getUsage is called) - partialMock is used because i just need to mock a single function:

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"]));

     
1

There are 1 best solutions below

6
On

Your exception in your partial mock, is because when you mock the BillingPlanService you do not intilize the userService due to it being a mock. You can simply set it on the mock and i think it should work in your context.

$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([]);
});