61

I want to verify that various date fields were updated properly but I don't want to mess around with predicting when new Date() was called. How do I stub out the Date constructor?

import sinon = require('sinon');
import should = require('should');

describe('tests', () => {
  var sandbox;
  var now = new Date();

  beforeEach(() => {
    sandbox = sinon.sandbox.create();
  });

  afterEach(() => {
    sandbox.restore();
  });

  var now = new Date();

  it('sets create_date', done => {
    sandbox.stub(Date).returns(now); // does not work

    Widget.create((err, widget) => {
      should.not.exist(err);
      should.exist(widget);
      widget.create_date.should.eql(now);

      done();
    });
  });
});

In case it is relevant, these tests are running in a node app and we use TypeScript.

Michael_Scharf
  • 33,154
  • 22
  • 74
  • 95
MrHen
  • 2,420
  • 2
  • 25
  • 39

3 Answers3

97

I suspect you want the useFakeTimers function:

var now = new Date();
var clock = sinon.useFakeTimers(now.getTime());
//assertions
clock.restore();

This is plain JS. A working TypeScript/JavaScript example:

var now = new Date();

beforeEach(() => {
    sandbox = sinon.sandbox.create();
    clock = sinon.useFakeTimers(now.getTime());
});

afterEach(() => {
    sandbox.restore();
    clock.restore();
});
Josh Kelley
  • 56,064
  • 19
  • 146
  • 246
Alex Booker
  • 10,487
  • 1
  • 24
  • 34
  • Awesome, that looks like it does what I needed. Once I get a finally working version I'll edit it into your answer and accept it. – MrHen Jul 23 '15 at 15:34
  • 1
    As of sinon v5, the `sinon` object is a sandbox which keeps track of the `sinon.useFakeTimers`, which can be invoked with `sinon.restore()`, allowing the `sandbox` and `clock` variables in the above to be dropped: https://sinonjs.org/guides/migrating-to-5.0.html#sinon-is-now-a-default-sandbox – pzrq Sep 20 '19 at 00:42
  • 1
    only a minor observation here: when you use a sandbox (as you should), you don't need to `clock.restore()`, as the sandbox handles it – Gerardo Lima Mar 24 '20 at 18:22
21

I found this question when i was looking to solution how to mock Date constructor ONLY. I wanted to use same date on every test but to avoid mocking setTimeout. Sinon is using [lolex][1] internally Mine solution is to provide object as parameter to sinon:

let clock;

before(() => {
    clock = sinon.useFakeTimers({
        now: new Date(2019, 1, 1, 0, 0),
        shouldAdvanceTime: true,
        toFake: ["Date"],
    });
})

after(() => {
    clock.restore();
})

Other possible parameters you can find in [lolex][1] API [1]: https://github.com/sinonjs/lolex#api-reference

Jarno
  • 6,243
  • 3
  • 42
  • 57
Anatoli Klamer
  • 2,279
  • 2
  • 17
  • 22
  • 2
    This answer is incredible and shows knowledge about the sinon/lolex API. it should be marked as correct, as it also solves the need of having a lot of `clock.tick()`s around the test. – yuriploc May 06 '20 at 20:21
18

sinon.useFakeTimers() was breaking some of my tests for some reason, I had to stub Date.now()

sinon.stub(Date, 'now').returns(now);

In that case in the code instead of const now = new Date(); you can do

const now = new Date(Date.now());

Or consider option of using moment library for date related stuff. Stubbing moment is easy.

realplay
  • 2,078
  • 20
  • 32
  • This worked for me except that `Date.now()` returns a timestamp, so I think the `now` method should be stubbed with the timestamp returned by `Date.now()` instead of creating a new Date with `new Date()`. Basically the second line should be: `const now = Date.now();` – adamgy May 06 '21 at 16:50