Jest fake timers with promises

Since [email protected] you can choose between two different fake timer implementations.

I found that jest.useFakeTimers('legacy') works with Promises using the flushPromises workaround, but it doesn't work with Date, whereas jest.useFakeTimers('modern') works with Date but not with Promises since await flushPromises() never resolves.

The best solution I found was to use @sinonjs/fake-timers instead, since that one works with both Promises and Date without any sort of workarounds or hacks:

import FakeTimers from "@sinonjs/fake-timers";

// Before tests:
const clock = FakeTimers.install();

// In tests:
await clock.tickAsync(100);

// After tests:
clock.uninstall();

The current best alternative is to use the async versions of fake-timers. So you would do

await clock.tickAsync(1000); // doesn't wait 1000ms but is async

Instead of calling clock.tick. Please see the answer below for more details.

At the moment, it's not supported

You're not doing anything wrong - it doesn't work at the moment - sorry. The following things have to happen before this will work from our end:

  • Jest needs to merge the ongoing work to merge lolex as their fake timer implementation here https://github.com/facebook/jest/pull/5171
  • Lolex needs to support pumping through promises - we've discussed this with the V8 team in a recent Node.js collaborator summit. That would expose a hook we'll use to allow doing something like advanceTimeByTime(100) and have that work with promises.

The problem in a gist is that the .then(spy) only gets called later.

As we are volunteers - there is no concrete timeline for these things. I hope SimenB does the merge in the coming 2-3 months and I'll follow up with the hook with the V8 team next month.

What you can do now

You can always write an async test:

// note this is an async function now
it('resolves in a given amount of time', async () => {
  // this is in a promise.reoslve.then to not 'lock' on the await
  Promise.resolve().then(() => jest.advanceTimersByTime(100));
  await timeout(100);
});

You can add expectations after the timeout if there is anything else you want to wait for.