2

I have a function wait

async function wait(time) {
  return new Promise((resolve) => setTimeout(resolve, time));
}

And I call this wait like this: await wait(5000); from a different function.

I am writing unit test cases and it always executes wait and each test case waits for 5s.

How do I stub the setTimeout using Sinon?

I tried:

  // Skip setTimeOut
  clock = sinon.useFakeTimers({
    now: Date.now(),
    toFake: ['setTimeout']
  });
  await clock.tickAsync(4000);
  await Promise.resolve();

But it didn't work.

prisoner_of_azkaban
  • 700
  • 1
  • 8
  • 26
  • 1
    Why would you? It's a standard-mandated function. Plus, this is Javascript, you don't need a mock library to overwrite a global function... – Jared Smith Mar 10 '20 at 12:32
  • @JaredSmith Unit test case execution is delayed by 50 seconds since I have 10 unit test case for that method. – prisoner_of_azkaban Mar 10 '20 at 12:39
  • So pass it a time of 0. It will fire on the next tick of the event loop. Then run your test on a 1 ms delay, instead of 4000. – Jared Smith Mar 10 '20 at 12:40
  • But passing 0 from unit test case won't affect the actual function behavior. – prisoner_of_azkaban Mar 10 '20 at 12:42
  • 1
    You are severely overthinking this. If its important to test the behavior with a 5 second delay, the you're going to have to eat those 5 seconds for every test that requires it. If it isn't, pass a zero and be done with it. If that can't be done with the way you have written the code, then rewrite the code. All that being said, `globalThis.setTimeout = (cb) => cb();` – Jared Smith Mar 10 '20 at 12:47
  • @JaredSmith Yes, you were right. I was overthinking this. – prisoner_of_azkaban Mar 10 '20 at 13:02

1 Answers1

1

Related post: setTimeout not triggered while using Sinon's fake timers

Github issue: https://github.com/sinonjs/fake-timers/issues/194#issuecomment-395224370

You can solve this in two ways.

  1. Consider whether your test case requires a delay of 5000ms.

The unit test should test the code logic, it's not integration tests. So, maybe you just need to make an assertion check the wait function is to be called with parameter. It's enough. We don't need to wait for 5000ms delay in the test case.

  1. If you insist want to use sinon.useFakeTimers() and clock.tick(5000).

From the related post, we can do it like this:

index.ts:

async function wait(time: number, clock?) {
  return new Promise((resolve) => {
    setTimeout(resolve, time);
    clock && clock.tick(time);
  });
}

export async function main(time, /* for testing */ clock?) {
  await wait(time, clock);
  console.log('main');
}

index.test.ts:

import { main } from './';
import sinon, { SinonFakeTimers } from 'sinon';

describe('60617715', () => {
  let clock: SinonFakeTimers;
  beforeEach(() => {
    clock = sinon.useFakeTimers();
  });
  afterEach(() => {
    clock.restore();
  });
  it('should pass', async () => {
    await main(5000, clock);
  });
});

unit test results:

  60617715
main
    ✓ should pass


  1 passing (9ms)
Lin Du
  • 88,126
  • 95
  • 281
  • 483