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.