Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stub moment.js constructor with Sinon

I am not able to stub the moment constructor when calling it with the format function to return a pre-defined string, here's an example spec that I would like to run with mocha:

it('should stub moment', sinon.test(function() {
  console.log('Real call:', moment());

  const formatForTheStub = 'DD-MM-YYYY [at] HH:mm';
  const momentStub = sinon.stub(moment(),'format')
                      .withArgs(formatForTheStub)
                      .returns('FOOBARBAZ');

  const dateValueAsString = '2025-06-01T00:00:00Z';

  const output = moment(dateValueAsString).format(formatForTheStub);

  console.log('Stub output:',output);
  expect(output).to.equal('FOOBARBAZ');

}));

I am able to see this output using console.log:

Real call: "1970-01-01T00:00:00.000Z"
Stub output: 01-06-2025 at 01:00

But then the test fails cause 01-06-2025 at 01:00 !== 'FOOBARBAZ' How can I properly stub that moment(something).format(...) call?

like image 356
TPPZ Avatar asked Feb 11 '16 13:02

TPPZ


3 Answers

I found the answer at http://dancork.co.uk/2015/12/07/stubbing-moment/

Apparently moment exposes its prototype using .fn, so you can:

import { fn as momentProto } from 'moment'
import sinon from 'sinon'
import MyClass from 'my-class'

const sandbox = sinon.createSandbox()

describe('MyClass', () => {

  beforeEach(() => {
    sandbox.stub(momentProto, 'format')
    momentProto.format.withArgs('YYYY').returns(2015)
  })

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

  /* write some tests */

})
like image 173
IanVS Avatar answered Nov 16 '22 22:11

IanVS


It's hard to tell from the description, but if the reason you are trying to stub the moment constructor(but not the rest of the lib functionality) is because you are trying to control what date Moment returns (for more reliable testing), you can do this using Sinon's usefakeTimer. Like so:

// Set up context for mocha test.
      beforeEach(() => {
        this.clock = date => sinon.useFakeTimers(new Date(date));
        this.clock('2019-07-07'); // calling moment() will now return July 7th, 2019.
      });

You can then update the date in the context of other tests that need to test the inverse logic around a specific date.

it('changes based on the date', () => {
  this.clock('2019-09-12');
  expect(somethingChanged).to.be.true;
});
like image 35
Lauren Avatar answered Nov 16 '22 22:11

Lauren


Try adding this to your test suite if all else fails;

moment.prototype.format = sinon.stub().callsFake(() => 'FOOBARBAZ');
like image 39
myol Avatar answered Nov 16 '22 20:11

myol