Lesson 23 / 25

Flaky Tests and Debugging

screen.debug, watch mode, test.only.

Find the non-determinism

A flaky test passes and fails without code changes, and teaches the team to ignore red builds. Common causes: un-awaited promises, real timers and dates, shared state between tests, order dependence, unmocked network calls, fixed sleeps instead of findBy/waitFor, and differences in time zone or locale. Debugging tools: watch mode (vitest or jest --watch) re-runs affected tests on save; test.only / describe.only focuses one test (remove before committing; lint rules can catch it); screen.debug() prints the current DOM, screen.debug(element) prints part of it; run a single file or name filter (-t 'pattern'); and attach a debugger (Vitest's VS Code extension, or node --inspect-brk for Jest). Retries (retry in Vitest, jest.retryTimes in Jest) can contain a flaky test while you fix it, but they hide the cause.

Debugging commands and helpers

Commands are shown, not run.

# watch mode: re-run related tests on every save
npx vitest              # Vitest watches by default in a terminal
npx jest --watch

# run one file, or tests whose name matches
npx vitest run src/cart/cart.test.ts
npx jest src/cart -t "applies coupon"

# shake out order dependence
npx vitest run --sequence.shuffle
npx jest --randomize     # recent Jest versions; check the docs

# inside a test (TypeScript):
#   screen.debug();                       // print the DOM
#   screen.debug(screen.getByRole('form'));
#   screen.logTestingPlaygroundURL();
#   test.only('focus on this one', ...)   // remove before committing

Quarantine with a ticket, not forever

If you must skip or retry a flaky test, link it to a tracked issue with an owner. A skipped test that nobody owns is a deleted test with extra steps.

Quick check: Which is a common cause of flaky component tests?

  • Waiting with a fixed sleep instead of awaiting findBy or waitFor
  • Using getByRole instead of getByTestId
  • Writing tests in TypeScript
  • Having a setup file
Answer

Waiting with a fixed sleep instead of awaiting findBy or waitFor — Wait for the condition, not for time.