Clean • Professional
Modern React applications often rely on asynchronous operations, such as API calls, timeouts, or promises. Testing components that perform async actions requires special handling to ensure tests wait for updates before asserting results.
Async Component Example
Suppose we have a component that fetches user data:
// UserProfile.jsx
import React, { useEffect, useState } from "react";
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
if (!user) return <p>Loading...</p>;
return <div>{user.name}</div>;
}
export default UserProfile;
Step 1: Mock the Fetch API
// UserProfile.test.jsx
import React from "react";
import { render, screen, waitFor } from "@testing-library/react";
import UserProfile from "./UserProfile";
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ name: "Jane Doe" }),
})
);
Step 2: Write Async Test Using waitFor
test("renders user name after API call", async () => {
render(<UserProfile userId={1} />);
// Wait for the async content to appear
const userName = await waitFor(() => screen.getByText("Jane Doe"));
expect(userName).toBeInTheDocument();
expect(fetch).toHaveBeenCalledTimes(1);
});
waitFor() waits until the callback stops throwing errors (useful for async updates)React Testing Library provides async-friendly queries like findByText, findByRole:
test("renders user name using findByText", async () => {
render(<UserProfile userId={1} />);
const userName = await screen.findByText("Jane Doe");
expect(userName).toBeInTheDocument();
});
waitForYou can also test components that use timeouts or delayed actions:
function DelayedMessage() {
const [message, setMessage] = React.useState("");
React.useEffect(() => {
setTimeout(() => {
setMessage("Hello after delay");
}, 1000);
}, []);
return <div>{message}</div>;
}
// Test
test("renders delayed message", async () => {
render(<DelayedMessage />);
const msg = await screen.findByText("Hello after delay");
expect(msg).toBeInTheDocument();
});
findByText waits for the message to appearglobal.fetch = jest.fn(() => Promise.reject("API error"));
test("handles API error", async () => {
render(<UserProfile userId={1} />);
// Custom error handling logic can be asserted here
});