8

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

13
  • why does the standard timeout feel weird? Commented Jan 15, 2014 at 19:55
  • if you were to use the $timeout you were able to call $timeout.flush(); with this you may test the pre timer conditions and the post timer conditions. But you do not want it :) Commented Jan 15, 2014 at 19:59
  • @rdodev: Well, I don't know if this is why it felt weird before, but I just tested and the expect() inside the timeout doesn't actually get called (I set the time to 10 seconds and reran the test, but it finished almost immediately). Commented Jan 15, 2014 at 20:01
  • @Michael: My timer has a method equivalent to flush(), but that's not what I'm trying to test. Commented Jan 15, 2014 at 20:01
  • In your last code snippet, when does the Timer() get triggered? At instantiation time? Commented Jan 15, 2014 at 20:04

1 Answer 1

12

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

Sign up to request clarification or add additional context in comments.

3 Comments

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 =)
"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.
setTimeout(function () {expect(flag).toBe(true)}), 1100); should be setTimeout(function () {expect(flag).toBe(true)}, 1100); - works great! :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.