Angular Unit Testing: Mocking Google Places Promise

April 02, 2019

The other day I was trying to use the fakeAysnc helpers in Angular to mock api responses from the Google places api. The methods use async await, but the tests kept failing over promises not getting resolved, then because the test couldn't reach the .then() of the mock. This led me to write the following:

beforeEach(() => {
    returned = undefined;
    spyOn(app, 'getLatLong').and.callThrough();
    spyOn(app._searchService, 'getPlaceDetails').and.returnValue(fakeAsync(
        return {
        then: () => {
        return { 
            geometry: {
                location: {
                    lat: () => {
                return 12345;
            },
            lng: () => {
                return -12345;
            }
            }
        }
        }
            }
        }
    ));
});

The thenable worked, sort of... Then() worked, but then nothing would never return the bits from the geometry.

So we got rid of the fakeAsync (at least I thought) and also removed the flush(). This was replaced this with on spy a Promise.resolve. In the actual test the async call worked when wrapped (this turns out to implicityly the fakeAsync zone. This lets me just await the spy.

So the modified solution for the test:

beforeEach(() => {
    returned = undefined;
    spyOn(app, 'getLatLong').and.callThrough();
    spyOn(app._searchService, 'getPlaceDetails').and.returnValue(Promise.resolve(
        {
            geometry: {
                location: {
                    lat: () => {
                        return 12345;
                    },
                    lng: () => {
                        return -12345;
                    }
                }
            }
        }
    ));
});

it('should call google places api with location string', ( async () => {
    returned = await app.getLatLong('longplaceidstring');
    console.log(returned);
    expect(returned.lat).toBe('12345');
}));

Interesting to note about wrapped async:

fakeAsync(() => {})

is similar to:

( async () => {} )

Insofar as both create an async zone in angular for testing. Wasn't expecting that until I tried to unwrap ( async () => { and my tests failed again.