Angular + Karma: Testing async functions

Problem

I have an angular service that does some async stuff (based on timers). One of the things you can do with a timer is define a ‘handler’ that fires when the timer expires (as in this pseudo-code):

flag = false;
timer = new Timer(1000); // ms
timer.handler = function () { flag = true };

In this trivial case, the timer would set flag to true after 1 second. How do I unit test this with Angular/Karma/Jasmine?

From reading the docs, I would have expected this to work:

... 
flag = false;
timer = new Timer(1000);
timer.handler = function () { flag = true };
expect(flag).toBe(false);
sleep(2)
expect(flag).toBe(true);
...

Rather than being morally upright, that test decided to fail with this:

ReferenceError: Can't find variable: sleep

After some reading, apparently I can’t use angular-scenario with Jasmine. Ok, I’m cool with that.

UPDATE : Per the comments, I tested my “working” settimeout method. It doesn’t ever get called.

So this works:

... 
flag = false;
timer = new Timer(1000);
timer.handler = function () { flag = true };
expect(flag).toBe(false);
setTimeout(function () { expect(flag).toBe(true) }, 2000);
...

But feels a little weird.

Question: Is there a better way?

Fun Trivia: Yep, I know about $timeout. I have Very Good Reasons(TM) for doing the things I did deep in the code mines, away from the light of day =)

Problem courtesy of: Sir Robert

Solution

Jasmine has a way to do async testing using waits() or waitsFor() and runs(). Look here.

Code would be something like:

... 
flag = false;
timer = new Timer(1000);
timer.handler = function () { flag = true };
expect(flag).toBe(false);
waitsFor( function() {
return flag;
}, "timer ran");
runs( function() {
expect(flag).toBe(true);
});
...

Note from OP

This is the right solution, so I marked it as accepted. I actually ended up implementing a sleep-like method based on this solution, and wanted to share in case it was helpful to others.

In the test file:

function loiter(ms) {
var loiter = true;
setTimeout(function () {loiter = false}, ms);
waitsFor( function () {return !loiter}, "Loitered too long", ms + 50);
}

it("should ...", function () {
flag = false;
timer = new Timer(1000);
timer.handler = function () {flag = true};
setTimeout(function () {expect(flag).toBe(true)}), 1100);

loiter(1200);
})

I hope this is useful! I’ll leave it as an exercise for the reader to figure out why I did it this way =)

Solution courtesy of: Alan Yackel

View additional discussion.

“Angular + Karma: Testing async functions” Posted first on ” JavaScript on Medium “
Author: Angular.js Recipes

Author: Pawan Kumar

Leave a Reply

Close Menu
%d bloggers like this:
Skip to toolbar