Sir Robert
Sir Robert

Reputation: 4976

Angular + Karma: Testing async functions

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 =)

Upvotes: 8

Views: 12178

Answers (1)

Alan Yackel
Alan Yackel

Reputation: 498

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 =)

Upvotes: 12

Related Questions