'How can I test google.maps.Geocoder?

Hi! I'm attempting to write a "simple" test to check for a react class component state change. Specifically, I'm testing if the lat(latitude) and lng(longitude) states change if Google successfully geocodes some string(address) that I send it. Here is an example of what I want to test (i.e. the lat and lng states being set to results[0].geometry.location.lat()):

getLatLong = (address) => {
    const that = this;
    var geo = new google.maps.Geocoder;

    geo.geocode({'address':address},(results, status) => {
        if (status == google.maps.GeocoderStatus.OK) {
            that.setState(
            {
                center: {
                    lat: results[0].geometry.location.lat(),
                    lng: results[0].geometry.location.lng(),
                },
            });
        }
    });
}

In my jest suite, I'm having issues writing my test and spying/mocking out the google.maps.Geocoder because the google library is never imported. It is attached via a script, like so:

<script async defer
    src=<%="https://maps.googleapis.com/maps/api/js?key=#{Rails.application.config.google_api_key}&callback=initMap"%>>
</script>

Just so its confirmed, the code works as intended after manual testing, but I receive errors inside my testing suite when attempting to spy like so:

let geocoderSpy = jest.spyOn(google.maps, 'Geocoder');

I receive an error like this:

  ● EventMap › getLatLong works as intended by getting coordinates of valid locations › calls the geocoder function

ReferenceError: google is not defined

  34 |         let geocoder;
  35 |         beforeEach(() => {
> 36 |             let geocoderSpy = jest.spyOn(google.maps, 'Geocoder');
     |                                          ^
  37 |             geocoder = jest.createSpy('Geocoder', ['geocode']);
  38 |             geocoderSpy.and.returnValue(geocoder);
  39 |         });

  at Object.google (app/javascript/__tests__/EventPage/EventMap.test.jsx:36:42)

So then, I followed up this problem and found this stackoverflow post and this answer that claimed to solve it by adding something like this to jest in the package.json file...

"globals": { "google": { } }

This also failed to solve my problem. I think the solution is to somehow find out how to import google, but not quite sure how to do that.... If anyone can help me out that would be greatly appreciated. I would love to learn how to test something like this. In advance, many thanks. Austin



Solution 1:[1]

It seems that you are not the only one having suffered through the terrors of testing Google maps with Jest. I found a lot of projects and conversations around. But they all seem to recommend mocking the Google maps library, by doing something similar to this :

const setupGoogleMock = () => {
  /*** Mock Google Maps JavaScript API ***/
  const google = {
    maps: {
      places: {
        AutocompleteService: () => {},
        PlacesServiceStatus: {
          INVALID_REQUEST: 'INVALID_REQUEST',
          NOT_FOUND: 'NOT_FOUND',
          OK: 'OK',
          OVER_QUERY_LIMIT: 'OVER_QUERY_LIMIT',
          REQUEST_DENIED: 'REQUEST_DENIED',
          UNKNOWN_ERROR: 'UNKNOWN_ERROR',
          ZERO_RESULTS: 'ZERO_RESULTS',
        },
      },
      Geocoder: () => {},
      GeocoderStatus: {
        ERROR: 'ERROR',
        INVALID_REQUEST: 'INVALID_REQUEST',
        OK: 'OK',
        OVER_QUERY_LIMIT: 'OVER_QUERY_LIMIT',
        REQUEST_DENIED: 'REQUEST_DENIED',
        UNKNOWN_ERROR: 'UNKNOWN_ERROR',
        ZERO_RESULTS: 'ZERO_RESULTS',
      },
    },
  };
  global.window.google = google;
};

// in test file.
beforeAll(() => {
  setupGoogleMock();
});

(source : https://github.com/hibiken/react-places-autocomplete/issues/189#issuecomment-377770674)

There is even an npm package for it! https://github.com/hupe1980/jest-google-maps-mock

Not sure if it will solve your problem, though, because this will only allow you to check if you have correctly called the right method from the API, not to check if you get the correct response from the API.

However, I don't know if it is advisable to actually do real API calls when testing such a functionality.

Solution 2:[2]

Inspired by the answer from Mathieu. I had to make a little change to adapt this solution for my project (Nuxt.js/Vue.JS).

I've replaced the Geocoder function with a mock of the Geocoder class.

const setupGoogleMock = () => {
  /** * Mock Google Maps JavaScript API ***/
  const google = {
    maps: {
      places: {
        AutocompleteService: () => {},
        PlacesServiceStatus: {
          INVALID_REQUEST: 'INVALID_REQUEST',
          NOT_FOUND: 'NOT_FOUND',
          OK: 'OK',
          OVER_QUERY_LIMIT: 'OVER_QUERY_LIMIT',
          REQUEST_DENIED: 'REQUEST_DENIED',
          UNKNOWN_ERROR: 'UNKNOWN_ERROR',
          ZERO_RESULTS: 'ZERO_RESULTS',
        },
      },
      Geocoder: class {
        public async geocode() {
          return Promise.resolve()
        }
      },
      GeocoderStatus: {
        ERROR: 'ERROR',
        INVALID_REQUEST: 'INVALID_REQUEST',
        OK: 'OK',
        OVER_QUERY_LIMIT: 'OVER_QUERY_LIMIT',
        REQUEST_DENIED: 'REQUEST_DENIED',
        UNKNOWN_ERROR: 'UNKNOWN_ERROR',
        ZERO_RESULTS: 'ZERO_RESULTS',
      },
    },
  }
  // @ts-ignore
  global.window.google = google
}
setupGoogleMock()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mathieu
Solution 2 DanielHefti