I am using React Testing Library to unit test my ReactJS code. There are several async events in the UI, like fetching data and displaying a new page on click of button. The React code is somewhat like this:
// Inside ParentComponent.tsx
const [isChildVisible, setChildVisibility] = useState(false);
const showChild = () => setChildVisibility(true);
return(
<>
<button data-testid="show-child" onClick={showChild}>Show Child</button>
{isChildVisible && <ChildComponent {..childProps}/>}
</>
)
Where ChildComponent
mounts, it fetches some data and then re-renders itself with the hydrated data. My unit test looks like:
jest.mock('../../../src/service'); // mock the fetch functions used by ChildComponent to fetch its data
describe('ParentComponent', () => {
test('renders ChildComponent on button click', async () => {
const screen = render(<ParentComponent />);
userEvent.click(screen.getByTestId('show-child'));
await (waitFor(() => screen.getByText('text rendered by child')));
});
});
When I run this test, I get the error "TestingLibraryElementError: Unable to find an element with the text: text rendered by child. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible."
.
I am not sure why it's happening, but one of the reason maybe that it's taking more than one second to hydrate and render the child component. Thus I want to change the default wait time for waitFor
, but I can't find a way to do it from the docs (the default wait time is one second). So is it possible to change the default wait time?
EDIT: Increasing the wait time is still causing the same error. So the issue is something else.
In order to use this function, we need to import it from @testing-library/react . import { waitFor } from '@testing-library/react'; As with the other async functions, the waitFor() function returns a Promise, so we have to preface its call with the await keyword.
The Testing Library family of libraries is a very light-weight solution for testing without all the implementation details. The main utilities it provides involve querying for nodes similarly to how users would find them. In this way, testing-library helps ensure your tests give you confidence in your UI code.
I found the answer here: React Testing Library - using 'await wait()' after fireEvent
TLDR: "You can not use wait with getBy*. getBy is not async and will not wait." Better is to use findBy*. This is the async version of getBy.
It's specified within the documentation. waitFor Documentation
function waitFor<T>(
callback: () => T | Promise<T>,
options?: {
container?: HTMLElement
timeout?: number //This is 1000ms. Change timeout here.
interval?: number
onTimeout?: (error: Error) => Error
mutationObserverOptions?: MutationObserverInit
}
): Promise<T>
//For 3 seconds.
await (waitFor(() => screen.getByText('text rendered by child'),{timeout:3000}));
The default timeout is 1000ms which will keep you under Jest's default timeout of 5000ms.
I had an issue similar to this when I was setting up testing for a test application. The way I fixed this issue was to force re-render the component.
In this case your code would look something like:
import {render, screen} from "@testing-library/react";
describe('ParentComponent', () => {
test('renders ChildComponent on button click', async () => {
const {rerender} = render(<ParentComponent />);
userEvent.click(screen.getByTestId('show-child'));
rerender(<ParentComponent />)
await (waitFor(() => screen.getByText('text rendered by child')));
});
});
I hope this works for you. Also to be noted that you can use the screen
export from the react testing library. It seems like there should be a way to do this automatically, but I haven't been able to find it.
Adding link to the rerender docs: https://testing-library.com/docs/react-testing-library/api/#rerender
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With