React · Frontend system thinking
How do you test React components with Testing Library?
Short answer
Render the component, query the UI the way a user would, and assert on visible behaviour. Prefer roles, labels and text over class names or internal state, and use userEvent for realistic interactions.
React Testing Library encourages tests that survive refactors. A test should not know whether a component uses useState, Redux or a context. It should know that a button named "Save" is visible, that clicking it calls the API, and that a success message appears.
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
test("submits the form and shows a confirmation", async () => {
const user = userEvent.setup();
const onSubmit = vi.fn().mockResolvedValue(undefined);
render(<SignupForm onSubmit={onSubmit} />);
await user.type(screen.getByLabelText("Email"), "asha@example.com");
await user.click(screen.getByRole("button", { name: "Sign up" }));
expect(onSubmit).toHaveBeenCalledWith("asha@example.com");
expect(await screen.findByRole("status")).toHaveTextContent("Check your inbox");
});Good testing habits
- Query by role, label, placeholder or text before falling back to test IDs
- Use
userEventinstead of firing synthetic events directly - Use
findByandwaitForfor asynchronous UI, not arbitrary sleeps - Mock the network at the boundary with MSW or a service mock
- Assert on what the user sees, not on hook calls or component internals
Do not test implementation details such as state variable names or whether a specific hook was called. Those tests break on refactors without catching user-facing regressions. For a custom hook with no UI, renderHook is appropriate, but most component behaviour is better tested through the rendered interface.
Follow-ups you should expect
Interviewers use these to check whether the first answer was understanding or memorisation.
- 1Why should tests avoid implementation details?
- 2When would you use renderHook?
- 3How do you test an async loading state?
- 4Why is getByRole usually preferred over getByTestId?
Say it in the interview
I test components through the interface a user sees. Testing Library gives me queries by role, label and text, and userEvent simulates realistic interaction. I assert on visible outcomes and mock the network at its boundary. I avoid checking internal state or hook calls, because those tests are brittle. If a component is hard to query accessibly, I treat that as a signal to improve the markup.
Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.
Related questions
Reading is step one
Now say it out loud.
Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live React mock interview and get scored feedback on depth, clarity and confidence.