spryce spryce - 6 months ago 26
Javascript Question

Function is called after promise is resolved but Jasmine fails the test. Why?

My application uses a service that returns a promise that is typically dependant on a whole bunch of other promises. I've refactored this into seperate named functions to make testing (and readability) easier. So in this case I just want to test that the run function does its job and calls the other functions.

e.g.

run() {
return myService
.connection
.then(this.namedFunction1)
.then(this.namedFunction2)
.then(this.namedFunction3)
.catch((error) => {
console.log("doh!", error.stack);
});


When I test that namedFunction1 has been called Jasmine fails, even though this is not the case. Here is a little code example I made up to keep things simple:

getString() {
return Promise.resolve("Heeeelp. Heeeelp!!");
}

printToConsole(string) {
console.log(string); // This works! but Jasmine says nay :(
}

myFunction() {
this.getString()
.then(this.printToConsole)
.catch((error) => {
console.log("Some error occurred", error);
});
}


...and the test:

it("should call the printToConsole function", function() {
spyOn(myClass, "printToConsole").and.callThrough(); //added the call through so it would print
myClass.myFunction();
expect(myClass.printToConsole).toHaveBeenCalled();
});


and the output...

> Started F[2016-05-16 11:32:31.898] console - Heeeelp. Heeeelp!!
>
>
> Failures: 1) MyClass myFunction should call the printToConsole
> function Message:
> Expected spy printToConsole to have been called. Stack:
> Error: Expected spy printToConsole to have been called.


I've tried adding the jasmine asynch done() function but this did nothing, and ultimately I'm resolving this promise immediately in the example.

So why or how can this test be failing?

Any help would be greatly appreciated. Thanks.

Answer

Because myFunction is an asynchronous operation. myFunction calls an async function and then returns immediately, after which the test assertion fires. At that point, printToConsole hasn't actually been called yet. You'll want to use Jasmine's async test support to run this test successfully.

You'll need to modify myFunction to return the promise so you can know when it's completed:

myFunction() {
  return this.getString()
  .then(this.printToConsole)
  .catch((error) => {
    console.log("Some error occurred", error);
  });
}

Then you'll modify your test to use the done function provided by Jasmine:

it("should call the printToConsole function", function(done) {
  spyOn(myClass, "printToConsole").and.callThrough(); //added the call through so it would print
  myClass.myFunction().then(function () {
    expect(myClass.printToConsole).toHaveBeenCalled();
    done();
  }).catch(done); // to make sure the test reports any errors
});

That should get things working.