user1261710
user1261710

Reputation: 2639

How to get karma/jasmine unit tests working with a callback after a promise?

I am writing an app in AngularJS 1.5, JavaScript and Cordova.

I want to write a unit test that will check to see if some code was executed after a promise.

Here is my codepen: https://codepen.io/aubz/pen/yrxqxE

I am not sure why but the unit test keeps saying this error: Expected spy attemptGeoClocking to have been called.

It's strange because the console log prints out so the function is actually being called.

it('if location services are on, proceed', function () {

    spyOn(CordovaDiagnostics, 'getLocationServicesStatus').and.callFake(function () {
        return Promise.resolve(true);
    });

    spyOn(Clocking, 'attemptGeoClocking').and.callFake(function () {});

    Clocking.geolocationClocking();

    expect(Clocking.attemptGeoClocking).toHaveBeenCalled();

});


function geolocationClocking() {

    CordovaDiagnostics
        .getLocationServicesStatus()
        .then(attemptGeoClocking)
        .catch(function () {});
}

function attemptGeoClocking() {
    console.log(' here ');
}

Upvotes: 0

Views: 614

Answers (1)

Frederik Prijck
Frederik Prijck

Reputation: 1509

Basically you're spying on the wrong functions. Let me rename a few things so it's more clear what you're doing:

function Clocking(CordovaDiagnostics) {

    return {
        geolocationClocking: geolocationClocking,
        attemptGeoClockingOUTER: attemptGeoClockingINNER//private API
    };

    function geolocationClocking() {

        CordovaDiagnostics
            .getLocationServicesStatus()
            .then(attemptGeoClockingINNER)
            .catch(function () {});
    }

    function attemptGeoClockingINNER() {
        console.log(' here ');
    }
}

And in the test:

spyOn(Clocking, 'attemptGeoClockingOUTER').and.callFake(function () {
      console.log('calling fake')
 });

As you can see, your code is spying on the OUTER but geolocationClocking is never calling the OUTER, it's using the INNER:

CordovaDiagnostics
            .getLocationServicesStatus()
            .then(attemptGeoClockingINNER)

You'll need to rework your code in such a way that it's using the same function internally as to the one you're stubbing in your test. Here's a working codepen: https://codepen.io/anon/pen/xeyrqy?editors=1111

Note: I've also replaced Promise.resolve with $q.when and added $rootScope.$apply(), this is needed to resolve the promises.

Adding the changes I made here, in case the codepen would ever disappear:

I've changed the factory to a service (while not necessary, I prefer using services in this case):

myApp.service("Clocking", Clocking);

function Clocking(CordovaDiagnostics) {

    this.geolocationClocking = function() {

        CordovaDiagnostics
            .getLocationServicesStatus()
            .then(() => this.attemptGeoClocking())
            .catch(function () {});
    }

    this.attemptGeoClocking = function() {
        console.log(' here ');
    }
}

Upvotes: 1

Related Questions