Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I test Ember.run.later with Sinon?

Tags:

ember.js

sinon

I have some Ember code that sets a timeout:

var MyObject = Ember.Object.extend({
  setFooToBarLater: function() {
    Ember.run.later(this, 'set', 'foo', 'bar', 500);
  }
});

I'd like to test that using Sinon's fake clock. Here's what I tried:

var clock = sinon.useFakeTimers();
var myObject = MyObject.create();
myObject.setFooToBarLater();
clock.tick(600);
expect(myObject.get('foo')).to.be('bar');

But the expect always runs before the set. I also tried wrapping the clock.tick in a run-loop:

Ember.run(clock, 'tick', 600);
like image 485
James A. Rosen Avatar asked Feb 20 '14 01:02

James A. Rosen


2 Answers

Beware that sinon.useFakeTimers (by default on) overrides the window.Date. That becomes an issue once you already have a scheduled Ember runloop and the useFakeTimers() is turned on. This is because Backburner (the Ember.js runloop implementation) setTimeout /core scheduling method/ tries hard to optimize calling window.setTimeout (look for executeAt in ember.js)

After going through the sinon source I made the following mod to let those two get along nicely (gist). It instructs sinon not to touch the window.Date and also patches sinon.test() to wait few more ticks after the test body which allows scheduled timers to get executed and async codes

sinon._originalUseFakeTimers = sinon.useFakeTimers;
sinon.useFakeTimers = function() {
    // make sure we don't override window.Date used in
    // Backburner.setTimeout() to optimize window.setTimeout() call numbers
    return sinon._originalUseFakeTimers.apply(this, [new Date().getTime(), "setTimeout", "setInterval", "clearTimeout", "clearInterval"]);
};

sinon._originalTest = sinon.test;
sinon.test = function(callback) {
    return sinon._originalTest.call(this, function() {
        callback.apply(this, arguments);

        // wait for further runloops to finish (2s is the mocha timeout)
        this.clock.tick(2000);
    });
};
like image 188
opichals Avatar answered Sep 28 '22 23:09

opichals


The answer appears to be not putting the clock.tick in a run-loop, but the call that actually invokes Ember.run.later. Thus, the following does work:

var clock = sinon.useFakeTimers();
var myObject = MyObject.create();
Ember.run(myObject, 'setFooToBarLater');
clock.tick(600);
expect(myObject.get('foo')).to.be('bar');
like image 38
James A. Rosen Avatar answered Sep 28 '22 23:09

James A. Rosen