Typically, you would write synchronous tests, as they are simple and get the work done. However, there are cases when using asynchronous (async) tests might be necessary or beneficial. The two most common cases are:
Using async tests when needed ensures your tests are reliable and simulate real-world conditions accurately.
Consider a basic asynchronous test for a user signing in with correct credentials:
There are several asynchronous utilities you might use in your tests.
findBy*
queriesThe most common are the findBy*
queries. These are useful when waiting for a matching element to appear. They can be understood as a getBy*
queries used in conjunction with a waitFor
function.
They accept the same predicates as getBy*
queries like findByRole
, findByTest
, etc. They also have a multiple elements variant called findAllBy*
.
Each query has a default timeout
value of 1000 ms and a default interval
of 50 ms. Custom timeout and check intervals can be specified if needed, as shown below:
Alternatively, a default global timeout
value can be set using the configure
function:
waitFor
functionThe waitFor
function is another option, serving as a lower-level utility in more advanced cases.
It accepts an expectation
to be validated and repeats the check every defined interval until it no longer throws an error. Similarly to findBy*
queries they accept timeout
and interval
options and have the same default values of 1000ms for timeout, and a checking interval of 50 ms.
If you want to use it with getBy*
queries, use the findBy*
queries instead, as they essentially do the same, but offer better developer experience.
waitForElementToBeRemoved
functionA specialized function, waitForElementToBeRemoved
, is used to verify that a matching element was present but has since been removed.
This function is, in a way, the negation of waitFor
as it expects the initial expectation to be true (not throw an error), only to turn invalid (start throwing errors) on subsequent runs. It operates using the same timeout
and interval
parameters as findBy*
queries and waitFor
.
Asynchronous tests can take long to execute due to the delays introduced by asynchronous operations. To mitigate this, fake timers can be used. These are particularly useful when delays are mere waits, such as the 130 milliseconds wait introduced by the UserEvent press()
event due to React Native runtime behavior or simulated 1000 wait in a API call mock. Fake timers allow for precise fast-forwarding through these wait periods.
Here are the basics of using Jest fake timers:
jest.useFakeTimers()
jest.useRealTimers()
jest.advanceTimersByTime(interval)
jest.runAllTimers()
jest.runOnlyPendingTimers()
Be cautious when running all timers to completion as it might create an infinite loop if these timers schedule follow-up timers. In such cases, it's safer to use jest.runOnlyPendingTimers()
to avoid ending up in an infinite loop of scheduled tasks.
You can use both built-in Jest fake timers, as well as Sinon.JS fake timers.
Note: you do not need to advance timers by hand when using User Event API, as it's automatically.