Testing synchronous specs is easy, but asynchronous testing requires some additional work.
For example, the code below fails because Jasmine evaluates the expect() piece before the testAsync() function has finished its work. The setTimeout() call forces a two second delay, but Jasmine has already moved on and failed the test before the setTimeout() completes:
With Jasmine async testing, we have to call the async code in the beforeEach() function that runs before each it() function block within a describe() function block.
We also have to let Jasmine know when the async function has completed by calling the special done() callback function Jasmine provides. Here, we are passing this special done() callback around so our code under test can invoke it.
This works, but changing the code under test to accept the done() callback argument and invoke it may not be realistic. Why would you change your code under test just to make the testing framework happy?
Instead, you can use promises and call the special Jasmine done() callback when your promise has resolved. Note that the Jasmine done() callback is NOT the same as the promise's done() callback. They just use the same function name.
Here, I'm using jQuery's $.Deferred() object for the promises, but this approach should work with any promises library.
You can even use the data returned from the promise in the test once it is resolved. Here we are passing the return value in the deferred.resolve() call:
But of course, real-world applications can't get away with simply testing with setTimeout and true/false flags, so here is a more real-world example.
It calls $.getJSON() to go fetch some public JSON data in the beforeEach() function, and then tests the returned JSON in the it() block to make sure it isn't an empty object or undefined.