amdilley
amdilley

Reputation: 249

setTimeout with Promise wrapper not working as expected with Jest async/await

Trying to do a relatively simple assertion with jest. I have the following test setup:

const sleep = ms => new Promise(res => setTimeout(res, ms));

it('should call callback after specified delay', async () => {
  const mockCallback = jest.fn();

  setTimeout(1000, mockCallback);

  expect(mockCallback).not.toHaveBeenCalled();

  await sleep(1000);

  expect(mockCallback).toHaveBeenCalled();
});

When I run the test fails with the following error:

Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

Obviously this is nowhere near that threshold. Any idea what I'm doing wrong?

[UPDATE] I realized previously I had called jest.useFakeTimers() before the test. After removing this and running the test again I still get a failure but it's not a timeout. Instead just simply

Expected mock function to have been called, but it was not called.

Note this is also the case when significantly increasing the sleep to 4000ms.

If instead I switch from setTimeout to

sleep(ONE_SECOND)
  .then(mockCallback);

the test passes. Jest clearly modifies how setTimeout interacts in parallel with Promises, but it's not obvious as to what is going on.

Upvotes: 4

Views: 1295

Answers (1)

Brian Adams
Brian Adams

Reputation: 45810

You just need to pass mockCallback as the first argument to setTimeout:

const sleep = ms => new Promise(res => setTimeout(res, ms));

it('should call callback after specified delay', async () => {
  const mockCallback = jest.fn();

  setTimeout(mockCallback, 1000);  // <= pass mockCallback as first argument

  expect(mockCallback).not.toHaveBeenCalled();  // Success!

  await sleep(1000);

  expect(mockCallback).toHaveBeenCalled();  // Success!
});

Upvotes: 3

Related Questions