0

I am using the spatie laravel calendar package to make api calls to Google Calendar I want to write some Feature tests but I want to mock the Event wrapper class so my tests don't actually make the API calls

I used Laravels Facades to

Controller

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

the Event:find method returns itself. Most of the data is in the googleEvent paramter.

Event Class

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

}

Test

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

What do I want this Mocked Facade to return in order to get back the response in the expected format.

I figured I could return a new instance of Event but the event class has a bunch of dependencies that I would need to mock up

If I return Self it gets back the mock Object but the googleEvent is null.

Edit

because i never set the google event. So one possible option is

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

but this still leaves the problem where I need to mock 5 classes just to get the result formatted correctly End Edit

What should I do to mock this class so that I can get the data back in the format it would come in if it were a normal request?

The result format looks like this

  +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

In order to mock this would require Mocking the Google_Service_Calendar_Event which requires the other 5 GOOGLE_SERVICE_CALENDAR_* classes and so on.

apokryfos
  • 38,771
  • 9
  • 70
  • 114
nickc
  • 1,193
  • 1
  • 6
  • 15
  • I had a brief look into the spatie/laravel-google-calendar package and the `Event` class doesn't have any dependencies. You could just create a new instance and return it from your mocked `find` call. But maybe I'm missing something. – Philip Weinke Aug 03 '20 at 20:06
  • In my experience, it is best to change the environment for running tests and inject your own implementations based on environment. But I am not a unit testing expert. – Kurt Friars Aug 03 '20 at 21:13
  • @PhilipWeinke the only problem with doing that is it leaves my googleEvent empty just like when I do an `andReturnSelf` but I did realize I can just modify the mock to get an actual return value there. – nickc Aug 04 '20 at 14:10
  • @KurtFriars so the class you are saying I should change is the Event class? Would I want to create a MockEvent class that gets used when running in a dev env? – nickc Aug 04 '20 at 14:13
  • 1
    @nickc You create a test version of your class, and then create a service provider that will check the environment and return your test implementation while testing, and real implementation otherwise. Again, I am a unit testing novice. I use this for feature tests when I have to rely on third party services and the like. – Kurt Friars Aug 04 '20 at 14:25
  • @nickc In your case I think you would need to override the packages service provider, which shouldn't be too hard. – Kurt Friars Aug 04 '20 at 14:28

1 Answers1

0

Using the suggestions from @KurtFrais I came up with a solution.

He suggested swapping the implementation to a mock object based on the environment. Instead of changing the implementation in the ServiceProvider I changed the implementation in the test method.

I created a mock object

class EventMock extends Event{

    public $googleEvent;

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


    ...
    ...
}

Then in the test method I swaped the implementation out like this

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


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

So when Laravel gets a new instance of the Event Class an EventMock object is used instead.

nickc
  • 1,193
  • 1
  • 6
  • 15