Angular + Karma: Testing async functions

11,723

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

Share:
11,723

Related videos on Youtube

Sir Robert
Author by

Sir Robert

Updated on September 14, 2022

Comments

  • Sir Robert
    Sir Robert over 1 year

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

  • Sir Robert
    Sir Robert over 10 years
    This is the right answer. Michael answered first in the comments above, but if he doesn't provide an answer soonishly, I'll accept yours as correct. Thanks =)
  • John Haugeland
    John Haugeland about 9 years
    "I'll leave it as an exercise for the reader to figure out why I did it this way =)" It would be nice if you'd finish explaining.
  • Thomas Kekeisen
    Thomas Kekeisen about 8 years
    setTimeout(function () {expect(flag).toBe(true)}), 1100); should be setTimeout(function () {expect(flag).toBe(true)}, 1100); - works great! :-)