ReactMiddleTechnical
Как тестировать React-компоненты? В чём разница между unit, integration и e2e тестированием?
Unit-тесты проверяют изолированные функции/компоненты, integration — взаимодействие нескольких частей вместе, e2e — весь пользовательский сценарий в реальном браузере. Для React типичный стек: Vitest + React Testing Library (unit/integration) и Playwright (e2e).
Три уровня тестирования
- Unit: одна функция, хук или компонент изолированно. Зависимости мокируются. Быстро, дёшево, хрупко при рефакторинге внутренностей.
- Integration: несколько компонентов/модулей в связке. Реальный DOM через jsdom, реальная логика, только внешние сервисы (API) мокируются. Баланс скорости и уверенности.
- E2E: реальный браузер, реальный бэкенд (или MSW-заглушка). Медленно, но проверяет весь путь пользователя.
Инструменты
- Vitest — быстрый test runner совместимый с Jest API, нативно понимает ESM и Vite-конфиг.
- React Testing Library (RTL) — рендерит компоненты в jsdom, поощряет запросы через роли/текст, а не через CSS-классы.
- MSW (Mock Service Worker) — перехватывает fetch/XHR на уровне Service Worker или Node; одни и те же хендлеры работают в браузере и в тестах.
- Playwright — e2e в Chromium/Firefox/WebKit, поддерживает трассировку, видеозапись, CI-режим.
Unit-тест: хук useCounter
// useCounter.test.ts
import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';
test('increment increases count', () => {
const { result } = renderHook(() => useCounter(0));
act(() => result.current.increment());
expect(result.current.count).toBe(1);
});
Integration-тест: форма с API-запросом
// LoginForm.test.tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import { LoginForm } from './LoginForm';
const server = setupServer(
http.post('/api/login', () =>
HttpResponse.json({ token: 'abc' })
)
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test('submits credentials and shows welcome', async () => {
render(<LoginForm />);
await userEvent.type(screen.getByLabelText('Email'), 'user@test.com');
await userEvent.type(screen.getByLabelText('Password'), 'secret');
await userEvent.click(screen.getByRole('button', { name: /войти/i }));
expect(await screen.findByText(/добро пожаловать/i)).toBeInTheDocument();
});
E2E-тест на Playwright
// e2e/login.spec.ts
import { test, expect } from '@playwright/test';
test('user can log in', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('user@test.com');
await page.getByLabel('Password').fill('secret');
await page.getByRole('button', { name: /войти/i }).click();
await expect(page).toHaveURL('/dashboard');
await expect(page.getByText('Добро пожаловать')).toBeVisible();
});
Когда что применять
- Чистая бизнес-логика, утилиты, хуки → unit.
- Форма с валидацией + вызов API → integration с MSW.
- Критичный пользовательский путь (регистрация, оплата) → e2e.
Подводные камни
- Тест через
getByTestIdпривязывает тест к вёрстке, а не к поведению — предпочитайтеgetByRole,getByLabelText. - Мок реализации, а не контракта: если мокируете внутренний модуль, тест не поймает смену API.
- Забытый
awaitв RTL: асинхронные запросы надо ждать черезfindBy*илиwaitFor, иначе тест проходит зелёным, не дождавшись ответа. - Нет
server.resetHandlers(): обработчики MSW «протекают» между тестами. - Слишком много e2e: медленные, нестабильные на CI. Пирамида тестирования: много unit, меньше integration, мало e2e.
- act() warnings: обновления состояния вне
actвызывают предупреждения и могут скрывать реальные баги. - jsdom не поддерживает layout: CSS-позиционирование, getBoundingClientRect, resize-события работают неправильно — для этого нужен e2e.
Common mistakes
- Тестировать имплементацию (
state, имена хуков) вместо поведения. - Мокать всю сеть в integration-тестах и пропускать ошибки сериализации.
- Гонять e2e на каждый push и убивать CI-бюджет.
What the interviewer is testing
- Различает ли цели уровней тестов.
- Знает ли актуальные библиотеки (RTL, Vitest, MSW, Playwright).
- Понимает ли принцип «testing user behavior».