Home > Mobile >  Testing a function which has a Promise and setTimeout, why is it timing out?
Testing a function which has a Promise and setTimeout, why is it timing out?

Time:11-24

I'm trying to test a function that has setTimeout inside a promise. However it keeps timing out.

This is the function:

export const sleep = async (duration: number): Promise<void> => {
  await new Promise<void>((resolve) => {
    setTimeout(resolve, duration);
  });

  if (process.env.NODE_ENV === "test") {
    console.log("sleep end");
  }
};

And this is my test:

import { sleep } from "../../helpers/utils";

console.log = jest.fn();
jest.useFakeTimers();

test("calls sleep with correct argument and calls console.log", async () => {
  const NODE_ENV = "test";
  const SLEEP_DURATION = "100";

  process.env = { ...process.env, NODE_ENV, SLEEP_DURATION };

  const timeoutSpy = jest.spyOn(global, "setTimeout");

  await sleep( SLEEP_DURATION);

  jest.runAllTimers();

  expect(sleep).toHaveBeenCalledWith( SLEEP_DURATION);
  expect(timeoutSpy).toHaveBeenCalledWith( SLEEP_DURATION);
  expect(console.log).toHaveBeenCalledWith("sleep end");
});

The problem is that when I try to run this the test fails and gives this message:

thrown: "Exceeded timeout of 5000 ms for a test.
    Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."

I've tried jest.setTimeout(10000) which just throws an error of Exceeded timeout of 10000ms ...

Any idea as to why this is happening? Or how to fix it?

Thanks!

CodePudding user response:

Here's a solution that gets closer to what you're going for. Importantly, you can't await the resolution of the promise using fake timers or it'll never resolve. Instead, you can call assign the return value of the sleep function to a variable, then run the timers, then await the variable.

I also adjusted your expect statement for the timeout spy since it takes two arguments. Finally, I removed your expectationt that sleep gets called with the duration because you're literally doing that in the test, so it doesn't seem worthwhile to make that assertion.

console.log = jest.fn();
jest.useFakeTimers();

test("calls sleep with correct argument and calls console.log", async () => {
  const NODE_ENV = "test";
  const SLEEP_DURATION = "100";

  process.env = { ...process.env, NODE_ENV, SLEEP_DURATION };

  const timeoutSpy = jest.spyOn(global, "setTimeout");

  const sleepFn = sleep( SLEEP_DURATION);

  jest.runAllTimers();

  // Now that we ran timers we can await the promise resolving 
  await sleepFn;

  // timeout takes two arguments
  expect(timeoutSpy).toHaveBeenCalledWith(
    expect.any(Function),
     SLEEP_DURATION
  );
  expect(console.log).toHaveBeenCalledWith("sleep end");
});
  • Related