Mocking setTimeout with Jest

Marek Rozmus
3 min readAug 6, 2022

--

Photo by Who’s Denilo ? on Unsplash

The title of this post may be a little misleading as we won’t write any mocks but we need to use some functionality that Jest provides. Let’s start!

Here is an example of a simple component:

The whole code can be found in this GitHub repository: https://github.com/marekrozmus/blog_mocking_settimeout_with_jest

It is component with three buttons and every click handler contains a setTimeout usage.

Writing the (incorrect) test may result in:

  • very fast test run but with incorrect results— the test skips all the setTimeout logic, it just doesn’t wait until setTimout finishes
  • it will take long time (but 5 seconds at most )— the test is wating for setTimeout to finish but in our case it is longer (10 seconds) than default test’s duration allowed in Jest. We will get a following error:
thrown: “Exceeded timeout of 5000 ms for a test.

We could increase timeout for unit tests but it is not a good practice. Usually when test exceeds timeout, it means that it is broken. Most probably async / await is missing somewhere or some API calls are missing mocks.

What to do with setTimeout?

Jest has a built in mechanism to handle such situation — the timer mocks. When we enable them we can “fast-forward time” inside the test.

Use fake timers

We need to place the testing code between jest.useFakeTimers() and jest.useRealTimers().

The first one is setting up fake timers and the second one restores the original JS behaviour.

Example 1 — setTimeout calls some API

The simplest case. The setTimeout callback is calling some function.

We just tell the Jest that all timers should complete. After that we can test if the callback logic was executed.

Example 2 — setTimeout updates component’s state

In this case we can encounter the “not wrapped in act(…)” error.

not wrapped in act(…)

This is because the setTimeout callback is updating the component’s state. The Jest’s method that complete the timers needs to be wrapped in act.

Example 3 — setTimeout throws an exception

The callback method is throwing an exception after 10 seconds. In this case we need to pass a function to expect as following:

In above example first we expect that after 9 seconds there won’t be an exception. We expect it after the timer’s finish.

fireEvent vs userEvent

There is one more thing worth to mention. The main difference in writing test when using fireEvent or userEvent to click the button.

When using fireEvent we get synchronous call to click. No additional setup is needed to test setTimeout with fake timers.

With userEvent it is different. Since version 14.0.0 the APIs always return a Promise. Because of that we need to make the whole test async and await on clicking with userEvent.

Other thing is that we need to setup the userEvent instance to properly behave with Jest’s fake timers. We need to set it up as following:

Conclusion

That’s it. Thank you for reading and leave me a comment if you have some question or proposal for making this post better.

Check also my other posts — happy testing :)

Check also my other posts about unit testing:

--

--