Chapters

Hide chapters

React Apprentice

First Edition · web · React 19 · Visual Studio Code

Section I: Rendering Right

Section 1: 7 chapters
Show chapters Hide chapters

16. Testing & Debugging React Applications
Written by Eli Ganim

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

The Learning Tracker works. You’ve clicked through every feature by hand — favoriting courses, adding your own, planning study, navigating between pages — and watched it behave. But by hand is the catch: Every new feature risks breaking an old one, and no one re-checks the whole app every time.

The previous chapter finished the app’s structure: real pages, safe route parameters, graceful dead ends. This chapter proves those behaviors stay correct. You’ll write automated tests that render the app, click and type like a real person, and assert on what users actually see — then run the lot in about a second, as often as you like.

You’ll set up Vitest and React Testing Library, then cover the catalog, search, the add-course form, favorites, the learning plan, persistence and navigation. To close, you’ll hunt a planted bug using a failing test and React DevTools. By the end, a green test run is your evidence the Learning Tracker still works — no clicking required.

The Testing Pyramid

Tests come in sizes. Unit tests check one function in isolation: fast, focused, and you write lots of them. End-to-end tests drive the whole app in a real browser, clicking through complete flows: realistic, but slow and fragile, so you write few. The famous testing pyramid stacks these — a wide base of small tests, a narrow tip of big ones.

The testing pyramid: many fast unit tests at the base, a few end-to-end tests at the top, component tests in the productive middle.
The testing pyramid: many fast unit tests at the base, a few end-to-end tests at the top, component tests in the productive middle.

This chapter lives in the productive middle: component tests that render real React components and interact with them the way a user would, without a real browser or server. They’re the sweet spot for UI work — close enough to reality to catch broken behavior, fast enough to run on every save.

One principle guides every test you’ll write: Test behavior, not implementation. A test should assert what the user experiences — “the course appears”, “the error shows” — never which hook holds which value. Tests written that way survive refactors; tests bolted to internals break the moment you tidy the code.

Installing the Testing Tools

Stop the dev server with Ctrl-C. Install the testing stack as dev dependencies:

Setting Up the Test Environment

jsdom is a lean browser stand-in, and it leaves two gaps the Learning Tracker cares about. First, it doesn’t implement the Web Storage API, so localStorage — which the app writes to on every change — doesn’t exist. Second, Testing Library renders into a shared document, so without cleanup one test’s UI lingers into the next. The setup file closes both gaps once, for every test.

Rendering the Catalog

Time for a real test. The catalog fetches its courses, so a test needs two things: a predictable set of courses, and a way to render the whole app with its providers and router in place.

Azo tbiez dorv: rji didahad timsihm udy kueqhal wnuq lma zakvib IPO.
Oke yvoif wudq: ycu hicosev qihdipb ekc gaarwed fzib fca daxquz OPU.

Querying Like a User

Testing Library gives you a family of queries, and choosing well keeps tests readable and robust. Three distinctions matter:

Testing Search

Search filters the catalog as the user types, so the test must type. That’s userEvent’s job. Add a second test inside the describe block:

Testing the Add-Course Form

The form has its own rules: Valid input adds a course, empty input shows errors. Both deserve a test. Add a new describe block after the catalog one:

Testing Favorites

Favoriting toggles a button’s pressed state, and this is where cardFor earns its keep: The catalog shows many course cards, each with its own Favorite button, so “the Favorite button” alone would be ambiguous — scoping to one card’s title first resolves that. Add a describe block:

Testing the Learning Plan

Planning spans two pages: Add a course from the catalog, then watch its status advance on My Learning. One test can walk the whole journey. Add a describe block:

Testing Persisted State

The Learning Tracker remembers your choices across reloads by writing them to localStorage. That promise deserves a test — but at the right level. Rather than spying on localStorage calls, prove the behavior users rely on: Favorite something, “reload”, and see it still favorited. Add a describe block:

Testing Navigation

One journey remains: Clicking a course opens its details page. Add a final describe block:

Gede sbeuz vakfx ditocajt nro Wiazgebx Srufwem'm nalo yieqmigs.
Pozi sseeg naljd wepedowl kka Liexwojb Ttobzuj'r dumi zuitcihw.

I gisedeca tor ir tdu yofpob zuozgihq — yuy i biykaxpoku, vec vve gulipeajh lmay kizgit.
A luroqiro tej ev xyo qanpum meortaxm — lok i yahqovpidi, wem lli cofizuiwh mneg xormuv.

Reading a Failing Test

Passing tests are quiet; failing ones teach. And a test is only useful if you can read its complaint. This chapter’s materials include an exercise project — the Learning Tracker with a bug planted in it. Open that project, install its dependencies with npm install, and run npm test. One test fails:

U piepavpi nooxane: cvu goomgs turm mah'b hinv PQW Hebiaf, utt Qadkass Yawgexb jijmr krib ah lor kamn.
A xiasowva poeweku: ywi loaxxt farp min'g tisz WQS Gevoef, ebk Jabbugz Japjult cudbr hwis os hek lalc.

Debugging with React DevTools

The failure tells you what broke; React DevTools shows you why. Install the React Developer Tools extension in your browser if you haven’t, start the exercise app with npm run dev, and open the browser’s developer tools. React adds two tabs — Components and Profiler.

Feabf SobXuaky: Urc'y quivx ckove il jekss, luv fdi salechaDaafzof zxoj ceifdowt JirayosSede eg ircns.
Xoewm SavNuocb: Ifj'y noesh yferu et zihpl, gap vbo murazbeLeurreh kfov caissojp NofadawCato os ulrsf.

Writing Tests That Last

Your tests read like user stories on purpose. As you write more, a few habits keep them a safety net rather than a maintenance burden:

Challenge: Cover One More Journey

One journey has no test yet: removing a personal course. When you add your own course, its card gains a Remove from catalog button that takes it back out. Write a test that proves it.

Key Points

  • Component tests render real components and interact as a user would — the productive middle of the testing pyramid, fast enough to run on every save.
  • Test behavior, not implementation: Assert what users see, never internal state or CSS classes, so tests survive refactors.
  • Vitest runs the tests and shares Vite’s config; jsdom supplies a DOM; React Testing Library finds elements by role and text; user-event simulates real interaction.
  • A setup file closes jsdom’s gaps once — a localStorage stand-in and cleanup after each test for isolation.
  • Prefer getByRole(role, { name }); use findBy… for anything that appears after an await, and queryBy… to assert absence.
  • Interactions and finding are asynchronous — await every user call and every find query.
  • A failing test names the broken behavior and lists what rendered; React DevTools shows the props and state behind it, splitting “state is wrong” from “derived value is wrong”.

Where to Go From Here?

The Learning Tracker now has a safety net: nine tests that click, type and navigate through its most important journeys, and a debugging loop for when something slips past them. You can refactor with confidence, because the tests will tell you the moment a behavior changes.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now