Assert The (Un)Presence of Elements in React Testing Library
/ 4 min read
Last Updated:TL;DR
- Check Element is Present:
test("displays a greeting", async () => { render(<Component greeting="Hello There" />);
expect(screen.getByText("Hello There")).toBeVisible();});
- Check Element is Present After a Delay (asynchronous operation, includes setState re-render):
test("displays a greeting", async () => { render(<Component />);
await user.click(screen.getByText("Load Greeting"));
expect(await screen.findByText("Hello There")).toBeVisible();});
- Check Element is Not Present:
test("hides user details", async () => { render(<Component />);
expect(screen.queryByText("Username")).not.toBeVisible();});
- Check Element is Not Present After a Delay (asynchronous operation, includes setState re-render):
test("hides user details after collapsing section", async () => { render(<Component />);
expect(screen.getByText("Username")).toBeVisible();
await user.click(screen.getByRole("button", { name: /hide user details/i }));
await waitFor(() => { expect(screen.queryByText("Username")).not.toBeVisible(); });});
- Check Element is Not Present After a Delay (asynchronous operation, includes setState re-render):
test("hides user details after collapsing section", async () => { render(<Component />);
expect(screen.getByText("Username")).toBeVisible();
await user.click(screen.getByRole("button", { name: /hide user details/i }));
await waitForElementToBeRemoved(screen.queryByText("Username")); // Followed by asserting on the presence of other elements.});
Asserting an Element is Present
Asserting the appearance of an element is dependent upon whether we expect changes to the DOM to happen immediately (synchronously) or asynchronously (delayed).
If we expect the element to be in the DOM unimpeded by a delay, we can use queries:
- get*
However, if we expect expect the element to be in the DOM after a delay (dependent upon some asynchronous operation or setState triggered re-render), we can use queries:
- find*
- get* (inside a waitFor)
findBy
findBy
queries work when you expect an element to appear but the change to the DOM might not happen immediately.Advice: use
find*
any time you want to query for something that may not be available right away.find*
looks cleaner and will output a nicer error message.
Async methods are used to deal with asynchronous code. These async methods aren’t limited to network calls. Remember that in React, state updates are asynchronous. This means that elements appearing or disappearing in the DOM in response to a user action are doing so asynchronously and therefore must be asserted against using asynchronous methods.
Imagine you are performing some filtering client side, and you expect one item to show and the other to be filtered out. This is how you could write your test:
it("should filter entries", async () => { const user = userEvent.setup();
user.click(await screen.findByRole("button", { name: /apply/i }));
await waitFor(() => expect(screen.getByText(fakeIssueOneTitle)).toBeVisible(), { timeout: 1500 });});
It should be noted that this code is the same as below because findBy
methods are a combination of getBy
and waitFor
utilities.
expect(await screen.findByText(fakeIssueOneTitle)).toBeVisible(), {}, { timeout: 1500});
Asserting an Element is NOT Present
To assert an element is not present in the DOM the queryBy*
query should be used. Unlike the getBy*
or findBy*
queries, queryBy*
does not throw an error if no elements match. It’s crucial to prove an element isn’t there because we’re counting on it not showing up in the query. Throwing an error when that happens misses the point.
queryBy...
: Returns the matching node for a query, and returnnull
if no elements match. This is useful for asserting an element that is not present.findBy...
: Returns a Promise which resolves when an element is found which matches the given query. The promise is rejected if no element is found […]
The only reason the
query*
variant of the queries is exposed is for you to have a function you can call which does not throw an error if no element is found to match the query (it returnsnull
if no element is found). The only reason this is useful is to verify that an element is not rendered to the page.
References
- Mastering Asynchronous Testing with React Testing Library: Clear example on testing loading states.
- Best Practices for Writing Tests with React Testing Library: Great advice for writing meaningful tests with React Testing Library.
- Presence Check Patterns with React Testing Library/Jest: Good summary of assertion patterns. Pretty much copied in the TL;DR section.
- How to Use React Testing Library to Wait for Async Elements | Step-by-Step Guide: Sort of explains the extra waitForElementToBeRemoved utility.