1
votes

I've got the following function in my AngularJS controller:

service.getPlaceByAddress = function(address) {
    return $q(function(resolve, reject) {
        geocoder().geocode({'address': address}, function(result, status) {
            // gets called
            if (status === google.maps.GeocoderStatus.OK) {
                return resolve(result);
            }

            return reject();
        });
    });
};

I want to test this piece of code, but the then function won't get called. But the geocode function gets called definitely.

it('returns an error if the data service returns no results', function(done) {
    GoogleMaps.getPlaceByAddress('Testlocation').then(function() {
        done();
        // gets never called
    });

    $scope.$digest();
});

Instead im getting an async timeout:

Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

Edit

When I'm returning the resolve directly, it works as expected:

service.getPlaceByAddress = function(address) {
    return $q(function(resolve, reject) {
        return resolve();
        //geocoder()...
    });
};

So I think, the problem is in the callback of the geocoder. This is strange, because the code works perfectly in the browser, but not in the jasmine test...

1
The issue is the done argument. See this: stackoverflow.com/questions/22604644/…Luca
@Luca: Sadly this is not the solution. If I remove the done argument, the promise doesn't resolve either... :/roNn23

1 Answers

2
votes

I think the problem is that you are calling a real asynchronous service instead of dummying it out in your unit test. If you have a dummy service that completes a promise immediately then it is sufficient for the test to call $scope.$digest() once to trigger the then() callback, but if its a real asynchronous call the digest will be called before the remote server has a chance to actually send back any data.

The best solution is to dummy out the asynchronous part of the test. So in this test you would put a stub in for geocoder().geocode() which when called with TestLocation as an argument simply completes immediately successfully, or fails, or do a test with each.

Alternatively you could have a loop that waits until the request really has had a chance to complete, but that's bad style for a unit test as they should be self-contained and not depend on a remote server.

From your comment:

The blog post you linked shows you how to mock the call to geocoder, but in that blog post it just verifies that the geocoder method has been called, it isn't actually testing that the service promise is resolved, and the spy that is used just returns a value it doesn't trigger the callback.

So I think you need to build on what they've done: add a spy that actually triggers the callback, but everything connected to promises is asynchronous so you will then need to call $digest() to force the promise to complete.