9

I'm writing some JavaScript module that is meant to running in browser (client side, not server side). This module is using Google Maps JavaScript API.

I want to cover my code by unit tests. I also want my tests to be isolated. I found several vcr-like JS libraries for recording and mocking HTTP-requests that Google Maps Api is producing. But all of them are for Node.JS (because PhantomJS doesn't support using fs module). Also Node.JS has more rich and readable backtraces than PhantomJS has.

So I'm wondering how to include Google Maps Javascript API to my tests with Node.JS-based test runner and how to write test for my code?

P.S. I'm not stick with some certain JS unit-test library. It can be Jasmine, QUnit or any other.

P.P.S It not necessary should be Node.JS runner. If there are another options it is completely OK!

P.P.P.S. My goal is to avoid following things:

  1. to avoid dependency on internet connection and corresponding delays in tests
  2. to avoid failing tests because of changing some GEO data on Google servers. For example: if I use directions, I don't really care if it 2000 meters or 2001 meters, I just wanna know, that I get some appropriate data from Google and perform some calculations with it.

P.P.P.P.S. Thanks to @MichaelGeary answer we know that Google saves only 3 versions of their API. But I'm not focusing only on Google Maps, I chose it in that question because of it's popularity/ I have the same question applied to any other maps api like Yandex.Maps, Leaflet (with openstreet), Bing and etc. Most of them don't delete old APIs, so I can fix version and rely on not changing internal API and HTTP requests.

Also I want to avoid mock hell, because my code is quite complex and uses a lot of geo objects of different kind. So it will not be easy to mock all of them and then support that code. It looks like unbearable thing.

My idea is to fix version of API for some time (in Google case for not so long time) and rely on persistence of internal HTTP requests format. And from time to time delete all recorded data to be sure that everything is still OK in real world.

I want myself to be one who control when I should fix my test. I don't wanna Google to break my tests at random moment of time.

Community
  • 1
  • 1
petRUShka
  • 9,812
  • 12
  • 61
  • 95
  • One thing you definitely *don't* want to do is use a library that records and replays the HTTP requests that the JavaScript Maps API uses. These request URLs and responses are undocumented, private to the Maps API code, and subject to change at any time. Any tests you write should be based only on the documented API, otherwise you will find your tests failing arbitrarily when Google changes the API internals. – Michael Geary Jun 21 '15 at 15:43
  • @MichaelGeary , my goal is to avoid several things: 1) to avoid dependency on internet connection and corresponding delays 2) to avoid failing tests because of changing some GEO data on Google servers. For example: if I use directions, I don't really care if it 2000 meters or 2001 meters, I just wanna know, that I get some appropriate data from Google and perform some calculations with it. So I don't want to replay HTTP requests on production or even in tests. I just want to mock all HTTP requests to make my tests isolated. – petRUShka Jun 21 '15 at 17:17
  • This sounds more to me like functional testing. Indeed the Phantomjs itself does not have the API for that but the [Casper.js](http://casperjs.org/) would be good. – Risto Novik Jun 21 '15 at 17:42
  • @RistoNovik I've meant exactly unit-testing. I want to test some methods/functions that is invisible for end-user. So I just wanna to wirtie down something like this: `assert extractDistance(route) == 5` where extractDistance is my method and route is Google Maps API object. – petRUShka Jun 21 '15 at 17:53
  • @petRUShka, the casperjs also has support for the testing framework similar to Mocha, http://docs.casperjs.org/en/latest/modules/tester.html – Risto Novik Jun 21 '15 at 18:06
  • @RistoNovik, what are the benefits of Casperjs in unit testing of code with Google Maps API inside? I just see yet another JS test runner. Please, correct me if I'm wrong. – petRUShka Jun 21 '15 at 18:36
  • Mocking the HTTP requests won't do you any good, for the same reason I mentioned above. These HTTP requests are an *implementation detail* of the Maps API and are subject to change at any time. Google changes the Maps API JavaScript code and changes the HTTP requests to match. If you want to mock something, it has to be the Maps API itself, because that is the only stable API. – Michael Geary Jun 22 '15 at 05:47
  • @MichaelGeary, I believe that I can fix Google Maps API version (like `v=3.19`) and rely on not changing internal API and therefore HTTP requests. Am I wrong? Do you have some counterexamples? https://developers.google.com/maps/documentation/javascript/basics?csw=1#Versioning – petRUShka Jun 22 '15 at 08:55
  • 1
    That is clever thinking, but alas it will only delay the inevitable. Google only keeps three versions of the API online at a time, and four times a year they retire the oldest version. They also make more frequent patch revisions, and there is no guarantee that they won't change the HTTP requests or other internals in those revisions. More details are in the answer below. – Michael Geary Jun 22 '15 at 17:11

1 Answers1

3

Recording or mocking the HTTP requests made by the Maps API is certainly an interesting idea!

Unfortunately, like any other undocumented features of the API, these requests are an internal implementation detail and are subject to change at any time. Google rolls out new versions of the API code four times a year, along with patch revisions as frequently as every few weeks. Any of the API internals—including the HTTP requests—could change from one version to the next, even in the patch revisions. The only thing they guarantee to be stable across versions or patches is the documented API.

Google does give you the ability to request a specific API version, but they don't keep old versions around for very long, and they don't keep old patch revisions at all. In fact, only three versions are live at any time. At this writing, these versions are available:

  • The experimental version, currently 3.21.4.
  • The release version, currently 3.20.12.
  • The frozen version, currently 3.19.19.

When the next experimental version (3.22) is rolled out, 3.21 will become the release version, 3.20 the frozen version, and 3.19 will be retired and no longer available.

The frozen version does have one difference from the release and experimental versions: it no longer receives any patches so it is completely stable. It should be safe to assume that the HTTP requests made by the frozen version won't change. But this only helps until that version is retired.

Here is a fiddle to experiment with requesting different API versions and displaying what version is actually loaded. The code looks like this:

<!DOCTYPE html>
<html>
<head>
    <title>Google Maps API version test</title>
</head>

<body>
    <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script src="http://maps.google.com/maps/api/js?v=3.18&sensor=false"></script>

    <div id="version"></div>

    <script>
        $('#version').text(
            'google.maps.version is ' +
            google.maps.version
        );
    </script>

</body>
</html>

The fiddle uses a v=3.18 parameter in the Maps API script URL to request version 3.18, but it actually loads version 3.19.19 at this moment. You can change the v= parameter to different values to see which API version gets loaded. (Besides the specific numbered versions, you can also use v=3 to get the current stable version, or v=3.exp to get the current experimental version.)

The sharp-eyed reader may note that the google.maps.version property this code displays is itself undocumented! But hey, this is experimental test code. :-)

It's fairly common for the HTTP requests to change from version to version, and even possible for them to change in a patch revision. As you can see from the list above, version 3.19 has gone through 19 patch revisions, and 3.20 has gone through 12 patch revisions.

If you want to write unit tests for your Maps API code, my suggestion is to mock the documented Maps API itself instead of mocking any of its internals. For example, your mock for google.maps.Map could check that its first parameter is a DOM node and that its second (optional) object parameter contains only known properties with legitimate values for those properties.

Of course the Maps API exposes quite a large number of objects, methods, and properties, but you don't have to mock the whole thing, just the parts of it you are using.

Michael Geary
  • 28,450
  • 9
  • 65
  • 75
  • Thanks you for such detailed answer! I just wanna say that: 1) I'm thinking that mocking API is almost ok in some simple cases, but in my case I should mock a lot (i'm working with directions, and steps and etc.)! It would be hell to support such code; 2) also I would faced with same problem: nobody guarantees persistence of data representation (e.g. addresses or coordinates of some objects). 3) I'm not focusing only on Google Maps, I chose them in that question because of it's popularity; I have the same question applied to any other map (most of them don't delete old apis). See update – petRUShka Jun 23 '15 at 15:05
  • @petRUShka, I'm curious if you ever got something working that you're satisfied with? Examples? – tim.rohrer Sep 14 '20 at 23:05