I'm following a tutorial on React testing. The tutorial has a simple component like this, to show how to test asynchronous actions:
import React from 'react'const TestAsync = () => {const [counter, setCounter] = React.useState(0)const delayCount = () => (setTimeout(() => {setCounter(counter + 1)}, 500))return (<><h1 data-testid="counter">{ counter }</h1><button data-testid="button-up" onClick={delayCount}> Up</button><button data-testid="button-down" onClick={() => setCounter(counter - 1)}>Down</button></>)}export default TestAsync
And the test file is like this:
import React from 'react';import { render, cleanup, fireEvent, waitForElement } from '@testing-library/react';import TestAsync from './TestAsync'afterEach(cleanup);it('increments counter after 0.5s', async () => {const { getByTestId, getByText } = render(<TestAsync />); fireEvent.click(getByTestId('button-up'))const counter = await waitForElement(() => getByText('1')) expect(counter).toHaveTextContent('1')});
The terminal says waitForElement
has been deprecated and to use waitFor
instead.
How can I use waitFor
in this test file?
Best Answer
If you're waiting for appearance, you can use it like this:
it('increments counter after 0.5s', async() => {const { getByTestId, getByText } = render(<TestAsync />);fireEvent.click(getByTestId('button-up'));await waitFor(() => {expect(getByText('1')).toBeInTheDocument();});});
Checking .toHaveTextContent('1')
is a bit "weird" when you use getByText('1')
to grab that element, so I replaced it with .toBeInTheDocument()
.
The react test library waitfor is a useful tool for testing React applications. It allows developers to wait for specific conditions or events to occur before asserting the expected outcome. This can be particularly helpful when testing asynchronous code or components that rely on external data or user interactions.
With the react test library waitfor, developers can wait for elements to be present in the DOM, for specific text to appear on the screen, or for events to be triggered. This ensures that the tests are not executed prematurely and that the expected behavior of the application is properly tested.
To use the react test library waitfor, developers need to import the necessary functions from the library and include them in their test code. They can then use these functions to wait for specific conditions or events to occur before proceeding with the assertion.
One common use case for the react test library waitfor is testing components that make asynchronous requests to an API. By using the waitfor function, developers can ensure that the component has received the data from the API before asserting the expected outcome.
In conclusion, the react test library waitfor is a valuable tool for testing React applications. It allows developers to wait for specific conditions or events to occur, ensuring that the tests are accurate and reliable. By using the waitfor function, developers can test asynchronous code and components that rely on external data or user interactions with ease.
Current best practice would be to use findByText
in that case. This function is a wrapper around act
, and will query for the specified element until some timeout is reached.
In your case, you can use it like this:
it('increments counter after 0.5s', async () => {const { findByTestId, findByText } = render(<TestAsync />); fireEvent.click(await findByTestId('button-up'))const counter = await findByText('1')});
You don't need to call expect
on its value, if the element doesn't exist it will throw an exception
You can find more differences about the types of queries here
Would it be also possible to wrap the assertion using the actfunction? Based on the docs I don't understand in which case to useact and in which case to use waitFor.
The answer is yes. You could write this instead using act():
import { act } from "react-dom/test-utils";it('increments counter after 0.5s', async() => {const { getByTestId, getByText } = render(<TestAsync />);// you wanna use act() when there is a render to happen in // the DOM and some change will take place:act(() => {fireEvent.click(getByTestId('button-up'));});expect(getByText('1')).toBeInTheDocument();});
Hope this helps.