4

I am writing a test for an async function which performs a series of tasks and at one point waits for 60 seconds before doing some more tasks. I am trying to use sinon.useFakeTimers() to skip those 60 seconds so that I can test the logic after the delay.

foo.js

module.exports.foo = async f => {
  // ... more code ...
  await new Promise((resolve, reject) => {
    setTimeout(resolve, 60000);
  });
  // ... more code ...
  f();
  // ... more code ...
};

test-foo.js

const sinon = require('sinon');
const expect = require('chai').expect;

const { foo } = require('./foo');

describe('Module Foo ', function() {
  it('call function after 1 minute', function() {
    var clock = sinon.useFakeTimers();
    const bar = sinon.stub();

    foo(bar);
    expect(bar.called).to.be.false;
    clock.tick(100000);
    expect(bar.called).to.be.true; // this one fails
  });
});

I tried putting sinon.useFakeTimers(); in various other places, but the Promise doesn't resolve and the stub I pass into foo doesn't get called.

Moglum
  • 113
  • 2
  • 7

1 Answers1

7

Make your test function async and await a resolved Promise after advancing your clock to give the Promise callback queued by the await in foo a chance to run before doing your assertion:

const sinon = require('sinon');
const expect = require('chai').expect;

const { foo } = require('./foo');

describe('Module Foo ', function() {
  it('call function after 1 minute', async function() {  // <= async test function
    var clock = sinon.useFakeTimers();
    const bar = sinon.stub();

    foo(bar);
    expect(bar.called).to.be.false;  // Success!
    clock.tick(100000);
    await Promise.resolve();  // <= give queued Promise callbacks a chance to run
    expect(bar.called).to.be.true;  // Success!
  });
});

For complete details see my answer here which uses Jest and Jest timer mocks but the concepts are the same and also apply to Mocha and Sinon fake timers.

Brian Adams
  • 43,011
  • 9
  • 113
  • 111