Ray
Ray

Reputation: 3060

How to use mock objects in php testing

I'm trying to learn how to test properly and am struggling to get my head around mocks in the scenario below. I don't seem to be able to mock a class.

The main class uses a number of component classes to build a particular activity. I can test the component on it's own and mock it correctly but when I try to integrate test within the main class it calls the real service not the mock service.

This is in a Laravel 5.5 app.

I have a base class:

class booking {

private $calEventCreator

    public function __construct(CalenderEventCreator $calEventCreator) {
       $this->calEventCreator = $calEventCreator;
    }
}

This is then extended by another class:

class EventType extends booking {

    //do stuff
}

The CalenderEventCreator relies on an external service which I want to mock.

class CalendarEventCreator {

    public function  __construct(ExternalService $externalService) {

        $this->externalService = $externalService;

    }
}

In my test I have tried to do the following:

public function test_complete_golf_booking_is_created_no_ticket()
{

    $this->booking = \App::make(\App\Booking\EventType::class);

    $calendarMock = \Mockery::mock(ExternalService::class);

    $calendarMock->shouldReceive([
        'create' => 'return value 1',
    ])->once();

    $this->booking->handle($this->attributes, 'booking');

}

But in trying to execute the test it's clear the ExyernalService is not using the mocked object.

I have tried re-arranging the code as follows:

$calendarMock = \Mockery::mock(Event::class);
    $calendarMock->shouldReceive([
        'create' => 'return value 1',
    ])->once();

    $this->booking = \App::make(\App\Booking\EventType::class);

    $this->booking->handle($this->attributes, 'booking');
}

and tried:

$this->booking = \App::make(\App\Booking\EventType::class, ['eventService'=>$calendarMock]);

But on each occassion the real service is called not the mock version

I'm learning this so apologies about fundamental errors but can someone explain how I should mock the external service correctly

Upvotes: 3

Views: 4610

Answers (1)

Gustavo Straube
Gustavo Straube

Reputation: 3871

Maybe there is a better way to achieve this but I'm using the following approach:

$calendarMock = \Mockery::mock(ExternalService::class);
$calendarMock->shouldReceive([
    'create' => 'return value 1',
])->once();

$this->booking = new \App\Booking\EventType($calendarMock);
$this->booking->handle($this->attributes, 'booking');

Instead of using the service container to resolve the class, I call the constructor directly passing the mocked service.

UPDATE

Looking for other ways to do that, I found this answer. Using that solution, your code would look like this:

$calendarMock = \Mockery::mock(ExternalService::class);
$calendarMock->shouldReceive([
    'create' => 'return value 1',
])->once();
$this->app->instance(ExternalService::class, $mock);

$this->booking = \App::make(\App\Booking\EventType::class);
$this->booking->handle($this->attributes, 'booking');

Upvotes: 2

Related Questions