Irbisa · Cheatsheet
Testing React
React Testing Library, Vitest and Jest, queries and user-event, mocking the network with MSW, testing hooks, Playwright end-to-end
What is React Testing Library, and what does "test behaviour, not implementation details" mean in practice?
import { render, screen } from '@testing-library/react';import userEvent from '@testing-library/user-event';import { Counter } from './Counter'; test('increments the count when the button is clicked', async () => { const user = userEvent.setup();…What is the difference between unit, integration and end-to-end tests in a React app, and how much of each would you write?
What is TDD, and when does writing the test first pay off in frontend work?
Vitest or Jest: which would you pick for a React project in 2026, and how do you set it up?
// vite.config.tsimport { defineConfig } from 'vitest/config';import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()],…What is the difference between
getBy,queryByandfindByqueries in React Testing Library?test('shows the user after loading', async () => { render(<Profile id={1} />); expect(screen.getByRole('progressbar')).toBeInTheDocument(); // there now expect(await screen.findByRole('heading', { name: 'Ada' })).toBeInTheDocument(); // appears later expect(screen.queryByRole('progressbar')).not.toBeInTheDocument(); // gone…Which RTL query should you reach for first, and why is
getByTestIdthe last resort?Find the bug: the test that checks an error message is gone fails with "Unable to find an element".
test('clears the error after a valid email', async () => { const user = userEvent.setup(); render(<SignupForm />); await user.type(screen.getByLabelText('Email'), 'nope'); await user.tab(); expect(screen.getByText('Enter a valid email')).toBeInTheDocument(); await user.clear(screen.getByLabelText('Email')); await user.type(screen.getByLabelText('Email'), 'ada@example.com'); expect(screen.getByText('Enter a valid email')).not.toBeInTheDocument();});What is the difference between
userEventandfireEvent, and why isuserEventpreferred?test('shows an error when the user leaves the email field empty', async () => { const user = userEvent.setup(); render(<EmailField />); // validates on blur await user.click(screen.getByLabelText('Email')); // pointer events, then focus await user.tab(); // focus moves on, so the field blurs…How do you test asynchronous UI, and what does the warning "An update to X inside a test was not wrapped in act(...)" mean?
Find the bug: this test passes even when the component never shows "Saved".
test('shows a confirmation after save', async () => { const user = userEvent.setup(); render(<Settings />); await user.click(screen.getByRole('button', { name: /save/i })); waitFor(() => { expect(screen.getByText('Saved')).toBeInTheDocument(); });});How do you mock network requests in component tests, and why is MSW usually better than mocking
fetchor the API module?// src/test/server.tsimport { http, HttpResponse } from 'msw';import { setupServer } from 'msw/node'; export const server = setupServer( http.get('/api/user/:id', ({ params }) =>…How do you test a component that needs providers such as a TanStack Query client, a router or a theme?
import { render } from '@testing-library/react';import userEvent from '@testing-library/user-event';import { QueryClient, QueryClientProvider } from '@tanstack/react-query';import { MemoryRouter } from 'react-router';import type { ReactElement } from 'react';…How do you test a custom hook?
import { renderHook, act } from '@testing-library/react';import { useCounter } from './useCounter'; test('increments and resets when the start value changes', () => { const { result, rerender } = renderHook(({ start }) => useCounter(start), { initialProps: { start: 5 },…What do
vi.mock,vi.fnandvi.spyOndo, and when is mocking a module a bad idea?import { vi, test, expect, afterEach } from 'vitest';import * as analytics from './analytics';import { checkout } from './checkout'; afterEach(() => vi.restoreAllMocks());…Find the bug: the test file crashes with a
ReferenceErrorbefore any test runs.import { vi, test, expect } from 'vitest';import { getUser } from './api';import { greet } from './greet'; const fakeGetUser = vi.fn().mockResolvedValue({ name: 'Ada' });vi.mock('./api', () => ({ getUser: fakeGetUser })); test('greets the user', async () => { expect(await greet(1)).toBe('Hello, Ada'); expect(getUser).toHaveBeenCalledWith(1);});How do you test code that depends on timers, such as a debounced search input?
afterEach(() => vi.useRealTimers()); test('searches 300 ms after the user stops typing', async () => { vi.useFakeTimers(); const onSearch = vi.fn(); const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });…When are snapshot tests useful in a React codebase, and why do many teams avoid large component snapshots?
What does code coverage tell you, and why is 100% coverage a poor target for a React codebase?
// vite.config.tsimport { defineConfig } from 'vitest/config'; export default defineConfig({ test: { coverage: {…What is Playwright, which flows would you cover with it, and how does it avoid the flakiness older E2E tools were known for?
import { test, expect } from '@playwright/test'; test('user signs in and sees the dashboard', async ({ page }) => { await page.goto('/login'); await page.getByLabel('Email').fill('ada@example.com');…How do you test Server Components and Server Actions, given that Testing Library cannot render an async Server Component?
import { beforeEach, expect, test, vi } from 'vitest';import { revalidatePath } from 'next/cache';import { auth } from '@/lib/auth';import { db } from '@/lib/db';import { renamePost } from './actions';…Your React test suite is slow and some tests fail randomly in CI. How do you find the causes and fix them?