Skip to content
tezvyn:
Menu

Handling Asynchronous UI with Testing Library

Source: testing-library.comMediumHow cards are made

Your test shouldn't race your UI. Use async helpers to wait for elements to appear, disappear, or change after an event. This is crucial for testing components that fetch data. The footgun is forgetting await on findBy or waitFor calls.

Why it exists

Modern UIs are dynamic. A user action, like clicking a button, often triggers asynchronous work like a network request. The UI doesn't update instantly; it updates after the work completes. Tests need a reliable way to wait for these updates to finish before asserting on the final state, avoiding flaky tests that fail due to timing issues.

The mental model

Think of your test as a patient user. A real user waits to see a result after clicking a button. Asynchronous test helpers like waitFor and findBy make your test behave like that patient user, polling the DOM until the expected change has occurred before moving on. It's about synchronizing your test's assertions with the component's asynchronous rendering lifecycle.

How it works

RTL provides three main async utilities. All return promises, so you must use await.

First, findBy queries (like findByText or findByRole) are the simplest. They are a shortcut, combining a getBy query with waitFor. They retry finding an element until it appears or a timeout is reached (default 1000ms).

Second, waitFor is a more general-purpose tool. You pass it a callback function containing your expectations. waitFor repeatedly runs this callback until it no longer throws an error, retrying every 50ms by default.

Third, waitForElementToBeRemoved is a specialized wrapper around waitFor. You give it an element or a function that returns an element, and it resolves its promise once that element is no longer in the DOM.

When to use it

Use async helpers whenever a DOM change doesn't happen synchronously. This is common after simulating user events that trigger async logic. Use findBy when you need to find an element that is not available right away. Use waitFor for more complex scenarios, like waiting for an assertion that isn't about an element's presence to pass. Use waitForElementToBeRemoved when you explicitly test that something disappears, like a loading spinner.

When not to use it

Do not use async helpers for things that are already in the DOM. If an element is present after the initial render, use a synchronous query like getByRole. Using findBy for a synchronous element adds unnecessary waiting and can slow down your test suite. The key is to use the right query for the right timing: getBy for present, queryBy for possibly absent, and findBy for eventually present.

One canonical example

Imagine a button that, when clicked, fetches data and displays it. The test needs to click the button and then wait for the data text to appear.

// Initial state: only the button is visible.

const button = screen.getByRole('button', { name: /Load Data/i });

// Simulate the user click, which triggers an async fetch.

fireEvent.click(button);

// Using getByText('Success!') would fail immediately. Instead, use // findByText, which waits for the element to appear.

const successMessage = await screen.findByText('Success!');

// Now that findByText has resolved, we can safely assert.

expect(successMessage).toBeInTheDocument();

Interview question

For testing an element that appears in the DOM only after an asynchronous operation, which Testing Library utility is the most appropriate and concise?

  • a.await waitForElementToBeRemoved(() => screen.queryByText('Loading...'))
  • b.await screen.findByText('Expected Text')Correct
  • c.screen.getByText('Expected Text')
  • d.await waitFor(() => expect(screen.getByText('Expected Text')).toBeInTheDocument())
Why?

findBy queries are designed specifically for elements that appear asynchronously, combining a getBy query with waitFor for conciseness. Using getByText (Option C) would fail immediately as the element isn't present synchronously. While waitFor with a callback (Option D) would also work, findBy is explicitly described as a simpler shortcut for this common scenario. Option A is for elements disappearing, not appearing.

Just read this? Test yourself on what you have been reading.

Read the original → testing-library.com

Put your scrolling time to good use

Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on react — each one lists the topics its interview covers.

See open roles