← Back to React Course | Chapter 12: Testing React Applications | Lesson 3 of 9

Rendering and Querying Elements

Render queries are like different ways of asking wheres the thing I'm looking for' — sometimes you search by name tag, sometimes by role, sometimes by what label is stuck on it.
Syntax
markup
render(<Component />);

screen.getByRole("role", { name: "label" }); // throws if missing
screen.queryByText("text"); // null if missing
await screen.findByText("text"); // waits for async content

getBy vs. queryBy vs. findBy

getBy* throws an error immediately if no matching element exists, making it ideal for asserting something IS present. queryBy* returns null instead of throwing, making it the right choice for asserting something is ABSENT. findBy* returns a Promise and waits, ideal for elements that appear after an async action.

Note:
  • A simple mental model:
  • getBy for 'this should exist right now'
  • queryBy for 'this should NOT exist'
  • findBy for 'this will exist soon'.
Warning: Using getBy* to check something is absent throws an unhelpful error instead of a clean assertion failure — use queryBy* + toBeNull() or not.toBeInTheDocument() instead.

Example: getBy vs. queryBy vs. findBy

markup
// Run in your local React project (npm install required)
import { render, screen } from '@testing-library/react';
import Alert from './Alert';

test('alert is not shown initially', () => {
  render(<Alert show={false} />);
  expect(screen.queryByText('Warning!')).not.toBeInTheDocument();
});

⚠️ This example uses an npm package with no CDN build available here — run this in your local React project.

Waiting for Async Content with findBy

When content appears after an asynchronous action (like a fetch resolving), findBy* queries return a Promise that repeatedly checks for the element until it appears or a timeout is reached, avoiding the need for manual waiting or arbitrary setTimeout calls in tests.

Note: Always await a findBy* query — forgetting the await means the test moves on before the element has actually appeared.
Warning: findBy* has a default timeout (usually 1000ms) — a genuinely slow-loading element might need an explicit longer timeout passed as an option.

Example: Waiting for Async Content with findBy

markup
// Run in your local React project (npm install required)
import { render, screen } from '@testing-library/react';
import UserProfile from './UserProfile';

test('shows user name after loading', async () => {
  render(<UserProfile userId={1} />);
  const name = await screen.findByText('Asha');
  expect(name).toBeInTheDocument();
});

⚠️ This example uses an npm package with no CDN build available here — run this in your local React project.

The Recommended Query Priority

Testing Library recommends a priority order for which query to reach for first: getByRole is preferred (it reflects real accessibility semantics), then getByLabelText for form fields, then getByText for general content, with test-ID-based queries as a last resort when nothing else fits.

Note: Following this priority order tends to naturally push your components toward better accessibility, since you're forced to think about roles and labels.
Warning: Reaching straight for getByTestId as a default habit skips the accessibility benefits the other queries encourage, and misses catching accessibility issues your tests could otherwise reveal.

Example: The Recommended Query Priority

markup
// Run in your local React project (npm install required)
// Priority order (most to least preferred):
screen.getByRole('button', { name: 'Save' });   // 1st choice
screen.getByLabelText('Email address');           // 2nd choice
screen.getByText('Welcome back');                 // 3rd choice
screen.getByTestId('save-button');                // last resort
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Using getByText for elements identified better by role, missing accessibility semantics the test could otherwise verify.
  2. Not knowing the difference between getBy, queryBy, and findBy — each behaves differently for missing or async elements.
  3. Overusing container.querySelector, bypassing Testing Library's user-centric query system entirely.
Chapter Summary
  • getBy* queries throw if zero or multiple matches are found — use for elements expected to exist right now.
  • queryBy* returns null instead of throwing — use to assert something does NOT exist.
  • findBy* returns a Promise, waiting for an element to appear — use for content that shows up asynchronously.
  • Query priority (recommended order): getByRole, getByLabelText, getByText, then more implementation-specific options.
Browser Support

Requires npm install @testing-library/react — runs in a Node test environment, not this browser sandbox.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.