Jest & React Testing Library (RTL) – Simplified


Testing is not just about verifying if code runs. It’s about verifying that your app behaves correctly from the user’s perspective. Jest + React Testing Library (RTL) gives a powerful combination to achieve this.
1. Filtering Tests in Watch Mode
When running Jest in watch mode, you can filter which tests run. This saves time when working on specific files or scenarios.
By file name (
p
) → only run tests from matching file(s).By test name (
t
) → run tests that match a string/regex in the test/describe name.All tests (
a
) → run the entire suite.Only changed files → default mode, runs tests related to changed files.
👉 Outside watch mode, you can use test.only
and test.skip
to explicitly include/exclude tests.
2. Grouping Tests with describe
Jest provides the describe(name, fn)
function to group related tests.
Helps structure tests into logical blocks.
describe.only
anddescribe.skip
work just liketest.only
andtest.skip
.Nested
describe
blocks allow hierarchical organization.Each file is considered one test suite.
Example:
describe('Login Component', () => {
test('renders login form', () => { /* ... */ });
test('shows error for invalid input', () => { /* ... */ });
});
3. Code Coverage
Jest can measure how much of your code is covered by tests.
Run with:
npm test -- --coverage --watchAll --collectCoverageFrom="src/**/*.{ts,tsx}"
Key metrics:
Branches → if/else conditions tested
Functions → all functions executed
Lines → all lines run
Statements → overall execution
You can enforce minimum thresholds in package.json
:
"jest": {
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 85,
"lines": 90,
"statements": 90
}
}
}
👉 Useful for CI/CD pipelines where you want to ensure minimum test quality.
4. What to Test vs What Not to Test
Test:
Component rendering
Rendering with props
Different states (loading, error, success)
User events (clicks, typing, etc.)
Don’t Test:
Implementation details (internal state logic)
Third-party library behavior
Things outside the user’s perspective
5. React Testing Library (RTL) Queries
RTL encourages testing like a real user. Instead of testing internals, you query elements as they appear in the DOM.
Common Queries
- getByRole (preferred)
screen.getByRole('textbox', { name: /username/i })
getByLabelText → finds input by
<label>
text.getByPlaceholderText → finds input by placeholder.
getByText → matches text inside
p
,div
,span
, etc.getByDisplayValue → finds input by its current value.
getByAltText → for images.
getByTitle → tooltips or elements with
title
.getByTestId → fallback with
data-testid
attribute.
👉 getAllBy*
queries return multiple elements, so you may need .toHaveLength()
assertions.
Matching Text
Queries support different matchers:
String →
"Login"
(exact match)Regex →
/login/i
(ignore case)Function →
(content) => content.startsWith('Hello')
Negative Queries
Use queryBy*
when you expect absence (so test won’t throw if not found).
expect(screen.queryByText(/loading/i)).not.toBeInTheDocument()
Async Queries
Use findBy*
for elements that appear asynchronously (default 1s timeout).
await screen.findByText(/welcome/i)
6. Debugging Helpers
screen.debug()
→ prints current DOM snapshot.logRoles(element)
→ lists accessible roles of elements.Testing Playground browser extension can help find best queries.
7. User Events
Simulating interactions with @testing-library/user-event
:
Click →
await user.click(button)
Typing →
await user.type(input, "Hello")
Tab navigation →
await user.tab()
Clear input →
await user.clear(input)
Select dropdown →
await user.selectOptions(select, "optionValue")
Upload file →
await user.upload(fileInput, file)
Clipboard actions →
copy
,paste
, etc.
👉 Prefer userEvent
over fireEvent
as it simulates more realistic user interactions.
8. Testing Custom Hooks
RTL provides renderHook
for hooks:
const { result } = renderHook(() => useCounter(), {
initialProps: { initialValue: 5 }
});
expect(result.current.count).toBe(5);
9. Mocking
Mock functions →
jest.fn()
Mock API calls → use MSW (Mock Service Worker) to simulate API endpoints.
Example handler:
rest.get('/posts', (req, res, ctx) => {
return res(ctx.status(200), ctx.json([{ id: 1, title: 'Mock Post' }]))
})
Useful for testing without hitting real APIs.
10. Static Analysis & Code Quality
Before tests, ensure code is consistent and clean.
ESLint → catch mistakes & enforce style
Prettier → consistent formatting
Husky + lint-staged → run checks before committing
TypeScript → type safety
Example setup:
npx husky-init && npm install
npm install --save-dev lint-staged prettier eslint
Add pre-push hook to run tests:
npx husky add .husky/pre-push "npm test -- --watchAll=false"
🔑 Key Takeaways
Use describe/test to organize tests logically.
Filter tests in watch mode for faster dev workflow.
Focus on user behavior, not implementation details.
Use RTL queries that match how users find elements.
Ensure code coverage thresholds in CI/CD.
Mock APIs & use static analysis tools for clean, reliable code.
Bonus
Why React Testing Library (RTL)?
Focus on user behavior → RTL encourages testing components the way a real user would interact with them (clicks, typing, text on screen), instead of testing implementation details.
Less fragile tests → Since it avoids testing private internals (like class names or state), your tests don’t break easily when refactoring.
Simple API → Queries like
getByText
,getByRole
, etc., make tests readable and closer to natural language.Community standard → RTL has become the go-to library for React testing, widely adopted in industry.
👉 Example: Instead of checking if a button has onClick
, you check if the button is clickable and what happens after click.
Why Jest?
All-in-one framework → Provides test runner, assertion library, mocking, and snapshot testing out of the box.
Zero config for JS/TS → Easy to start, especially with React, Node.js, or frontend projects.
Fast & isolated → Runs tests in parallel and isolates them for reliability.
Great ecosystem → Works seamlessly with RTL for unit & integration tests.
👉 Example: Write test("adds numbers", () => expect(sum(2,3)).toBe(5))
and Jest handles everything — runner, assertion, report.
Why Playwright?
End-to-End (E2E) testing → Unlike Jest/RTL (which test components in isolation), Playwright tests full apps in real browsers (Chromium, Firefox, WebKit).
Cross-browser automation → Ensures app works the same across browsers and devices.
Modern features → Auto-waiting, screenshots, videos, and parallel execution for stable E2E tests.
CI/CD friendly → Works well in pipelines to catch production-level issues.
👉 Example: Open app in browser, click login, enter creds, check if dashboard loads — exactly what a user would do.
Thanks to Codevolution
Subscribe to my newsletter
Read articles from Abheeshta P directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Abheeshta P
Abheeshta P
I am a Full-stack dev turning ideas into sleek, functional experiences 🚀. I am passionate about AI, intuitive UI/UX, and crafting user-friendly platforms . I am always curious – from building websites to diving into machine learning and under the hood workings ✨. Next.js, Node.js, MongoDB, and Tailwind are my daily tools. I am here to share dev experiments, lessons learned, and the occasional late-night code breakthroughs. Always evolving, always building.