The Testing Library Philosophy
Chapter 1 named the fix; Chapter 2 built the raw test-runner mechanics. This chapter introduces the actual tool built around that philosophy — React Testing Library (RTL) — and the philosophy itself, before any real component interaction begins in Chapter 4.
The Guiding Principle
Testing Library's own tagline, from creator Kent C. Dodds, states the whole philosophy in one line: "The more your tests resemble the way your software is used, the more confidence they can give you." Concretely, that means testing through the same interface a real user has — what's visible on screen, what can be clicked or typed into — never through a component's internal React state, props, or methods.
render(): Mounting a Component Into a Test DOM
render(...) mounts a component into a lightweight, jsdom-based test DOM and returns the screen object, used to query it. Older tools like Enzyme — React's previous, now-deprecated testing library, still referenced in a lot of older tutorials — deliberately exposed internals like .state() and .instance(). RTL deliberately does not provide any way to reach into a component's internals at all. That's a design choice, not a missing feature.
Querying by Role, Text, and Label
Every one of these queries something a real user (or assistive technology) actually perceives — an ARIA role, visible text, a form label — rather than a CSS class or internal prop, directly resolving Chapter 1's CSS-class gotcha. This connects concretely to web-accessibility1's ARIA material: writing accessible markup in the first place is what makes a component queryable by role or label at all. Accessibility and testability reinforce each other by construction, not by coincidence.
The getBy / queryBy / findBy Trio
| Prefix | If Not Found | Use When |
|---|---|---|
getBy... | Throws immediately | The element should already exist right now |
queryBy... | Returns null | Asserting something does NOT exist |
findBy... | Returns a rejected Promise (after retrying) | Waiting for async content to appear |
The Query Priority Guide
Testing Library's own documented, recommended order — not just this course's opinion:
- getByRole — first choice; matches how assistive technology and most users actually perceive an element
- getByLabelText / getByPlaceholderText / getByText — strong alternatives when a role query doesn't fit
- getByTestId — an explicit last resort, an escape hatch for when nothing else works, not a default habit
render() + screen
Mounts a component into a jsdom test environment; screen is the object every query is called on.
No Internals Exposed
Unlike Enzyme, RTL provides no way to reach a component's state/instance directly — by design.
getByRole First
The top of the official priority order — the query closest to how a real user or screen reader perceives the page.
getByTestId Last
Invisible to real users and assistive tech alike — a deliberate escape hatch, not a first choice.
getByRole or getByLabelText, that's frequently a real sign the markup itself isn't accessible enough — a missing label, a wrong or missing ARIA role. RTL's design turns "write testable code" and "write accessible code" into nearly the same habit, a direct, practical payoff of the site's own web-accessibility1 course.
data-testid attribute is invisible to real users and assistive technology alike — over-relying on it produces tests that pass even when a real user genuinely couldn't perceive or interact with the element the same way. It's not wrong to use occasionally, but it should be a deliberate last resort, not a habit — a common, real mistake among teams migrating from Enzyme's more implementation-focused style.
Coding Challenges
For a component rendering a "Save" button, write the getByRole query that would find it, and explain why getByRole('button', {name: 'Save'}) is preferred over getByText('Save').
📄 View solutionExplain which query prefix (getBy, queryBy, or findBy) you'd use for each: (a) confirming an error message is NOT shown before form submission, (b) confirming a submit button exists immediately after rendering, (c) confirming a "Success!" message appears after an async save completes.
📄 View solutionA component renders a div with class "error-message" containing the text "Invalid email" — but no ARIA role and no associated label. Explain why this is both a testing problem and an accessibility problem, and what change would fix both at once.
📄 View solutionChapter 3 Quick Reference
- Guiding principle: test the way a real user uses the software, never internal implementation details
- render(<Component />) — mounts into a jsdom test DOM; returns/enables the
screenquery object - RTL deliberately exposes no way to reach component internals (state/instance) — unlike the older, deprecated Enzyme
- Query priority: getByRole first, then getByLabelText/getByPlaceholderText/getByText, getByTestId as a last resort only
- getBy* — throws if missing; queryBy* — returns null if missing; findBy* — async, waits for appearance
- Hard-to-query markup is often a real accessibility gap, not just a testing inconvenience
- Next chapter: Testing React Components — rendering, firing events, conditional rendering, and snapshot testing's pitfalls