When using Sinon's useFakeTimers, access the real time and setTimeout

2,667 views
Skip to first unread message

Louis Chatriot

unread,
Feb 26, 2013, 10:00:23 AM2/26/13
to sin...@googlegroups.com
Hello,

I'm using Sinon's useFakeTimers to test a time-dependent module. When in "fake time" mode, I need to use the real setTimeout, meaning that I want to be able to delay the execution of a piece of code for 50 real-world ms.

Of course, using setTimeout doesn't work (even if I ask useFakeTimers to fake only "Date"), so I looked into the source to see that the real function is stored in the clock object as _setTimeout. My problem is that even calling clock._setTimeout doesn't work.

Thus my question: is this even possible? If yes what am I doing wrong?

Thanks in advance,
Louis

Christian Johansen

unread,
Feb 26, 2013, 12:45:29 PM2/26/13
to sin...@googlegroups.com
My advice is to keep a local reference to setTimeout before faking it:

var st = setTimeout;
sinon.useFakeTimers();

Using underscored properties in Sinon is _not_ recommended.

Christian

Louis Chatriot

unread,
Feb 26, 2013, 4:18:02 PM2/26/13
to sin...@googlegroups.com
Hey Christian,

Thanks for your quick reply! Keeping a reference to setTimeout before faking it is indeed the first thing I did, but it didn't work. I didn't have time to dig very deep in Sinon's source so I may overlook something simple ... Maybe the context can be useful in understanding this: I am using Sinon's fake timers in Mocha tests for my Nodejs application. Here is the relevant code, simplified so that it is readable:

var st = setTimeout
  , sinon = require('sinon')
  , clock
  ;

describe('Testing stuff', function() {
  beforeEach(function (done) { clock = sinon.useFakeTimers(some date); done(); });
  afterEach(function (done) { clock.restore(); done(); });

  it('a test', function (done) {
    st(function () {
      console.log("This should be displayed after 2s but it isn't the case");
      done();
    }, 2000);
  });
});

Thanks for your help,
Louis

Christian Johansen

unread,
Feb 27, 2013, 2:18:44 AM2/27/13
to sin...@googlegroups.com
Make sure that your local reference is indeed a reference to setTimeout,
and that it wasn't overwritten even before you stored it (log it or
something).

Christian

Louis Chatriot

unread,
Feb 27, 2013, 3:08:12 AM2/27/13
to sin...@googlegroups.com
I did check that too. Executing "console.log(st.toString())" gives me the same result as doing this with setTimeout when I don't use Sinon:

function () {
      var t = NativeModule.require('timers');
      return t.setTimeout.apply(this, arguments);
    }

Still, the st function doesn't work. When I use useFakeTimers(some date, "Date") so that setTimeout is not overwritten, logging it gives the expected result, but it also doesn't work. My guess is that Sinon messes up with the V8's timers module somehow, I don't see what else could be happening.

To add to the strangeness, setTimeout and st work as expected in the beforeEach section, when called after useFakeTimers. They don't work in the it section though.

Louis

Christian Johansen

unread,
Feb 27, 2013, 3:23:10 AM2/27/13
to sin...@googlegroups.com
That does not look like any native setTimeout I've seen, and it's not
Sinon's. Something else is happening.

Christian

Louis Chatriot

unread,
Feb 27, 2013, 4:53:31 AM2/27/13
to sin...@googlegroups.com
This is the usual setTimeout code from Node.js. It calls the setTimeout function from Node's timers module (https://github.com/joyent/node/blob/master/lib/timers.js). Which is why I'm baffled: the code is the usula one but it is still not working. But Sinon doesn't seem to do anything else than replace pointers to the native functions by pointers to his functions, so Sinon shouldnt have an effect on the inner workings of the timers module.

Louis Chatriot

unread,
Feb 27, 2013, 4:58:53 AM2/27/13
to sin...@googlegroups.com
I may have found the problem's root cause. The timers' setTimeout creates (https://github.com/joyent/node/blob/master/lib/timers.js, line 189) a new Timeout object. The object definition is at line 257 and you can see it depends on the Date object at line 261. Since Sinon modifies the Date object, it is probable this setTimeout doesn't work as for the fix I have still no clue but I think this is the right direction.

Louis

Christian Johansen

unread,
Feb 27, 2013, 5:05:57 AM2/27/13
to sin...@googlegroups.com
> I may have found the problem's root cause. The timers' setTimeout creates (
> https://github.com/joyent/node/blob/master/lib/timers.js, line 189) a new
> Timeout object. The object definition is at line 257 and you can see it
> depends on the Date object at line 261. Since Sinon modifies the Date
> object, it is probable this setTimeout doesn't work as for the fix I have
> still no clue but I think this is the right direction.

Hah, strange. You can avoid faking Date:

sinon.useFakeTimers("setTimeout", "clearTimeout");

Christian

Louis Chatriot

unread,
Feb 27, 2013, 5:18:35 AM2/27/13
to sin...@googlegroups.com
Unfortunately I have to fake Date, that was Sinon was so useful for me. I came up with this ugly workaround:

// This works even after Sinon takes over the time in a Node.js environment
// But it is blocking so only suitable for tests
// Not the exact same signature are the native setTimeout, you can't pass arguments to the callback
var myBlockingSetTimeout = (function (Date) {
  return function (cb, delay) {
    var beginning = (new myDate()).getTime()
      , now = (new myDate()).getTime();

    while (now < beginning + delay) {
      now = (new myDate()).getTime();
    }

    cb();
  }
})(Date);

If you put this definition before Sinon takes over the Date object, you have a blocking setTimeout function because we have a local reference to a Date object that works (that wasn't the case with setTimeout as changing the global Date object impacted setTimeout). This is only suitable for tests so this will work OK for me, but I think it will be difficult to fix this for you since you can't inject dependencies into Node's core modules, and the timers module doesn't expose its internal Timeout constructor.

Good luck with that ;)
Louis

Louis Chatriot

unread,
Feb 27, 2013, 5:21:02 AM2/27/13
to sin...@googlegroups.com
Ooops, of course the three 'myDate' in the return function should be 'Date' ...

Christian Johansen

unread,
Feb 27, 2013, 7:12:08 AM2/27/13
to sin...@googlegroups.com
You can make it non-blocking by using process.nextTick and Date. It
would even be possible to patch Sinon so that:

if node
overwrite setTimeout with implementation using process.nextTick + Date

Christian

Louis Chatriot

unread,
Feb 27, 2013, 7:49:28 AM2/27/13
to sin...@googlegroups.com
Indeed I hadn't thought about process.nextTick since I don't need it non blocking. So I guess patching Sinon is pretty easy then :)
Reply all
Reply to author
Forward
0 new messages