Back to Testing
Testing
medium
mid

How do you test React components and API flows using Jest and React Testing Library?

Yes — Jest/Vitest as the runner, Testing Library for components (queries by role, behavior over implementation). Mock fetch with MSW for API flows (intercept network, no `fetch` mocks). Test what the user sees: roles, accessible names, behavior on click/type. Avoid testing internal state or implementation details. Coverage on critical paths; E2E (Playwright) for golden flows.

4 min read·~10 min to think through

Stack

ToolPurpose
Jest / VitestTest runner + assertion library
React Testing LibraryRender + query React components
@testing-library/user-eventRealistic user interactions
MSW (Mock Service Worker)Mock network at the request level
PlaywrightEnd-to-end

Component test

tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

test('login form submits', async () => {
  const onSubmit = vi.fn();
  render(<LoginForm onSubmit={onSubmit} />);

  await userEvent.type(screen.getByLabelText(/email/i), 'a@b.c');
  await userEvent.type(screen.getByLabelText(/password/i), 'pass');
  await userEvent.click(screen.getByRole('button', { name: /sign in/i }));

  expect(onSubmit).toHaveBeenCalledWith({ email: 'a@b.c', password: 'pass' });
});

Queries by role and accessible name — same as a screen reader.

Query priority

  1. getByRole (accessible name, role).
  2. getByLabelText (form fields).
  3. getByPlaceholderText.
  4. getByText.
  5. getByTestId (last resort).

If you can't query by role, your component probably has an a11y problem.

API flow with MSW

tsx
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';

const server = setupServer(
  http.get('/api/user', () => HttpResponse.json({ id: 1, name: 'Sam' })),
);

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

test('shows user', async () => {
  render(<Profile />);
  expect(await screen.findByText('Sam')).toBeInTheDocument();
});

MSW intercepts at the network layer — no fetch mocks, no axios mocks. Same handlers work in Storybook / Playwright.

What NOT to test

  • Component internal state (expect(component.state.x)).
  • Specific class names / styles (unless that's the public API).
  • Hook order / implementation details.
  • Pure unrelated dependencies.

Async patterns

  • findBy* for async appearances (waits + retries).
  • waitFor for arbitrary async assertions.
  • waitForElementToBeRemoved for disappearance.

Mocking modules

ts
vi.mock('@/lib/analytics', () => ({ track: vi.fn() }));

Mock only at boundary modules (analytics, logger). Don't mock internal modules — refactor instead.

Coverage philosophy

  • High coverage on critical paths (auth, checkout, save).
  • Lower coverage on UI permutations (visual regression handles that).
  • E2E (Playwright) for happy-path flows through real-ish stack.

Common mistakes

  • Testing expect(wrapper.find('Button').props().onClick).toBeDefined() (Enzyme leftover style).
  • Mocking React Query / Redux internals.
  • Snapshot tests with huge JSON — diffs become noise.
  • Testing implementation details that change with refactors.

Tooling

  • @testing-library/jest-dom for matchers (toBeInTheDocument, toHaveAccessibleName).
  • --changed for fast CI on PR diff.
  • --watch locally for fast feedback.

Interview framing

"Vitest (or Jest) + Testing Library + MSW + Playwright. Query components by role and accessible name — same as a screen reader; reaching for getByTestId is a smell. User-event for realistic interactions. MSW for API flows — mock at the network layer so handlers are reusable across Storybook and Playwright. Don't test internal state or implementation; test what the user sees. Coverage focused on critical paths; visual regression and E2E pick up where unit tests stop. Refactor instead of mocking internal modules."

Follow-up questions

  • Why prefer getByRole over getByTestId?
  • What does MSW give you over fetch mocking?
  • When would you reach for E2E vs unit?

Common mistakes

  • Testing implementation details.
  • fetch mocks instead of MSW.
  • Huge snapshot tests.
  • Testing class names.

Performance considerations

  • Vitest parallelism + happy-dom faster than jsdom for many cases.

Edge cases

  • Async timing (use findBy and waitFor).
  • Strict Mode double-rendering effects in tests.
  • Portals and getBy queries.

Real-world examples

  • Testing Library docs, Kent C. Dodds' guides, MSW examples.

Senior engineer discussion

Seniors anchor on testing behavior, set coverage policy by criticality, and integrate MSW across test/Storybook/E2E.