Irbisa · Шпаргалка
Тестирование React
React Testing Library, Vitest и Jest, запросы и user-event, мок сети через MSW, тесты хуков, end-to-end на Playwright
Что такое React Testing Library и что на практике означает «тестировать поведение, а не детали реализации»?
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();…Чем отличаются unit-, интеграционные и end-to-end тесты в React-приложении и сколько каких вы бы писали?
Что такое TDD, и когда во фронтенде окупается написание теста первым?
Vitest или Jest: что вы выберете для React-проекта в 2026 году и как его настроить?
// vite.config.tsimport { defineConfig } from 'vitest/config';import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()],…Чем отличаются запросы
getBy,queryByиfindByв 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…Какой запрос RTL использовать в первую очередь и почему
getByTestIdостаётся крайним вариантом?Найдите баг: тест, проверяющий, что сообщение об ошибке исчезло, падает с «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();});Чем отличаются
userEventиfireEventи почему предпочитаютuserEvent?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…Как тестировать асинхронный UI и что значит предупреждение «An update to X inside a test was not wrapped in act(...)»?
Найдите баг: этот тест проходит, даже когда компонент так и не показывает «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(); });});Как мокать сетевые запросы в тестах компонентов и почему MSW обычно лучше, чем мок
fetchили API-модуля?// src/test/server.tsimport { http, HttpResponse } from 'msw';import { setupServer } from 'msw/node'; export const server = setupServer( http.get('/api/user/:id', ({ params }) =>…Как тестировать компонент, которому нужны провайдеры, например клиент TanStack Query, роутер или тема?
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';…Как протестировать кастомный хук?
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 },…Что делают
vi.mock,vi.fnиvi.spyOnи когда мокать модуль плохая идея?import { vi, test, expect, afterEach } from 'vitest';import * as analytics from './analytics';import { checkout } from './checkout'; afterEach(() => vi.restoreAllMocks());…Найдите баг: тестовый файл падает с
ReferenceErrorещё до запуска первого теста.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);});Как тестировать код, который зависит от таймеров, например поле поиска с debounce?
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 });…Когда снапшот-тесты полезны в React-кодовой базе и почему многие команды избегают больших снапшотов компонентов?
Что показывает покрытие кода (coverage), и почему 100% покрытия не стоит делать целью для React-кодовой базы?
// vite.config.tsimport { defineConfig } from 'vitest/config'; export default defineConfig({ test: { coverage: {…Что такое Playwright, какие сценарии вы бы им покрывали и как он избегает нестабильности, которой славились старые E2E-инструменты?
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');…Как тестировать Server Components и Server Actions, если Testing Library не умеет рендерить 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';…Набор тестов React медленный, а часть тестов случайно падает в CI. Как вы найдёте причины и исправите их?