Как смоделировать результат класса-оболочки API в PHPUNIT?

Я использую пакет календаря spatie laravel для вызовов API в Календарь Google. Я хочу написать несколько тестов функций, но я хочу смоделировать класс-оболочку Event, чтобы мои тесты на самом деле не выполняли вызовы API.

Я использовал фасады Laravels для

Контроллер

class CalendarEventController extends Controller
{
    public function show($calendarId, $event_id)
    {
        return response()->json(\Facades\Event::find($event_id, $calendarId));
    }
}

метод Event:find возвращает сам себя. Большая часть данных находится в параметре googleEvent.

Класс событий

class Event
{
    /** @var \Google_Service_Calendar_Event */
    public $googleEvent;

}

Тест

public function test_event(){
    
        \Facades\Event::shouldReceive('find')->once()->andReturn(?);
        $response = $this->json('GET','/event/1/1');
        $response->assertJson([
        ...
        ]);
}

Что я хочу, чтобы этот Mocked Facade возвращал, чтобы вернуть ответ в ожидаемом формате.

Я подумал, что могу вернуть новый экземпляр Event, но у класса event есть куча зависимостей, которые мне нужно будет смоделировать.

Если я верну Self, он вернет фиктивный объект, но googleEvent будет нулевым.

Изменить

потому что я никогда не устанавливал событие Google. Так что один из возможных вариантов

$mock = \Facades\Event::shouldReceive('find')->once()->andReturnSelf()->getMock();
$mock->googleEvent = {Whatever}

но это все еще оставляет проблему, когда мне нужно имитировать 5 классов только для того, чтобы правильно отформатировать результат End Edit

Что я должен сделать, чтобы смоделировать этот класс, чтобы я мог вернуть данные в том формате, в котором они были бы получены, если бы это был обычный запрос?

Формат результата выглядит так

  +googleEvent: Google_Service_Calendar_Event    
    +id: "XXX"
    +kind: "calendar#event"
    ...
    ...
    +"creator": Google_Service_Calendar_EventCreator 
    +"organizer": Google_Service_Calendar_EventOrganizer 
    +"start": Google_Service_Calendar_EventDateTime 
    +"end": Google_Service_Calendar_EventDateTime 
    +"reminders": Google_Service_Calendar_EventReminders

Чтобы имитировать это, потребуется имитировать Google_Service_Calendar_Event, для которого требуются другие 5 классов GOOGLE_SERVICE_CALENDAR_* и так далее.


person nickc    schedule 03.08.2020    source источник
comment
Я кратко изучил пакет spatie/laravel-google-calendar, и класс Event не имеет никаких зависимостей. Вы можете просто создать новый экземпляр и вернуть его из своего издевательского вызова find. Но, возможно, я что-то упускаю.   -  person Philip Weinke    schedule 03.08.2020
comment
По моему опыту, лучше всего изменить среду для запуска тестов и внедрить свои собственные реализации на основе среды. Но я не специалист по модульному тестированию.   -  person Kurt Friars    schedule 04.08.2020
comment
@PhilipWeinke единственная проблема с этим заключается в том, что мой googleEvent остается пустым, как когда я делаю andReturnSelf, но я понял, что могу просто изменить макет, чтобы получить там фактическое возвращаемое значение.   -  person nickc    schedule 04.08.2020
comment
@KurtFriars, так что вы говорите, что класс, который я должен изменить, - это класс Event? Хотел бы я создать класс MockEvent, который будет использоваться при работе в среде разработки?   -  person nickc    schedule 04.08.2020
comment
@nickc Вы создаете тестовую версию своего класса, а затем создаете поставщика услуг, который будет проверять среду и возвращать вашу тестовую реализацию во время тестирования, а в противном случае — реальную реализацию. Опять же, я новичок в модульном тестировании. Я использую это для тестирования функций, когда мне приходится полагаться на сторонние сервисы и тому подобное.   -  person Kurt Friars    schedule 04.08.2020
comment
@nickc В вашем случае, я думаю, вам нужно переопределить поставщика услуг пакетов, что не должно быть слишком сложно.   -  person Kurt Friars    schedule 04.08.2020


Ответы (1)


Используя предложения @KurtFrais, я нашел решение.

Он предложил заменить реализацию на фиктивный объект на основе среды. Вместо изменения реализации в ServiceProvider я изменил реализацию в тестовом методе.

Я создал фиктивный объект

class EventMock extends Event{

    public $googleEvent;

    public static function find($eventId, string $calendarId = null): Event
    {
        $self = new self();
        $self->googleEvent = $self->fake();
        return $self;
    }


    ...
    ...
}

Затем в тестовом методе я поменял реализацию следующим образом.

public function test_event()
{
    $this->instance(Event::class,new EventMock());


    $response = $this->json('GET','/event/1/1');
    $response->assertJson([
    ...
    ]);
}

Поэтому, когда Laravel получает новый экземпляр класса Event, вместо него используется объект EventMock.

person nickc    schedule 05.08.2020