Lesson 26 of 80
The dominant modern approach tests components the way a user actually experiences them — what's on screen and what happens when you interact with it — not internal implementation details like state variable names.
13 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 2
A test checks `wrapper.state('count') === 1` after a click. Why does React Testing Library's philosophy consider this a bad practice?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
React Testing Library's guiding principle: the more your tests resemble how users use your app, the more confidence they give you. Concretely, that means querying the rendered output the way a user would find things — by visible text, label, or ARIA role — instead of reaching into a component's internals.
import { render, screen, fireEvent } from "@testing-library/react";
test("clicking increments the counter", () => {
render(<Counter />);
fireEvent.click(screen.getByRole("button", { name: /increment/i }));
expect(screen.getByText("Count: 1")).toBeInTheDocument();
});
Notice what's absent: no reference to useState, no reaching into the component instance, no checking an internal variable. The test only knows what a real user would know — there's a button labeled "Increment," clicking it, and text that changes.
render(<Counter />) — mounts the component into a virtual DOM, just like a browser would.
wrapper.state('count') breaks the moment you refactor useState to useReducer — even though the user-visible behavior never changed. Querying by what's on screen survives that refactor.fireEvent vs userEvent: fireEvent dispatches a single low-level DOM event; userEvent (a separate, recommended library) simulates a full realistic interaction (focus, then keydown, then input, then blur) — closer to what actually happens when a real user types.waitFor/findBy* queries exist for asynchronous UI — content that appears after a fetch resolves needs an async query (await screen.findByText(...)), not an immediate getByText, which would run before the data arrives and fail."How would you test this component?" is asked constantly, and the strongest answers explicitly reject testing state/props directly in favor of testing behavior — querying and interacting the way a real user would.