Stubbing out time

JavaScript code on various platforms makes heavy use of the global timer functions – setTimeout() etc. – and when testing such code it’s often good to be able to take control of the clock to drive a test scenario forward. This means that, rather than setting your own timeouts in tests to wait for things to happen, you can tell the clock to tick forward and run your timeouts. This speeds your tests up and reduces some of the line noise of running asynchronous tests.

JS.Test.FakeClock lets you stub out JavaScript’s clock and move time forward yourself. To use it, you need to add the following hooks to your tests:

JS.ENV.TimerSpec = JS.Test.describe('Timers', function() { with(this) {
    include(JS.Test.FakeClock);

    before(function() { this.clock.stub() });
    after(function() { this.clock.reset() });

    // Your tests here
}});

Calling clock.stub() replaces the global time functions – Date(), setTimeout(), clearTimeout(), setInterval() and clearInterval() – with versions that run on a fake clock that you control. They work just like the real versions, only that time is now controlled by you rather than by your computer.

When using FakeClock, timers will not execute unless you advance the clock. To make time tick forward, use the clock.tick() method:

// Advances time 1000 milliseconds
this.clock.tick(1000);

Let’s take a quick example. This test sets a timeout to change the value of a string, and we use FakeClock to test it:

JS.ENV.TimerSpec = JS.Test.describe('Timers', function() { with(this) {
    include(JS.Test.FakeClock);

    before(function() { this.clock.stub() });
    after(function() { this.clock.reset() });

    before(function() { with(this) {
        this.string = 'foo';
        JS.ENV.setTimeout(function() { string = 'bar' }, 100)
    }});

    it('changes a string', function() { with(this) {
        assertEqual('foo', string);
        clock.tick(100);
        assertEqual('bar', string);
    }});
}});

Note that we’re calling setTimeout() on JS.ENV, a reference to the global object – otherwise known as window in web browsers. This is because the binding of the global variable setTimeout is not changed if you modify window.setTimeout in Internet Explorer, so if you want your code to pick up the patched version you need to use the global object explicitly. If you’re not concerned about using FakeClock in IE you can omit the JS.ENV reference.