Unit-тесты React-компонентов с React Testing Library

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Unit-тесты React-компонентов с React Testing Library
Средний
~3-5 дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1358
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1250
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    956
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1188
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    929
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    947

Разработка Unit-тестов для компонентов (React Testing Library)

Вы написали React-компонент, он работает, но после рефакторинга падают старые тесты? Знакомая ситуация, когда тесты проверяют внутреннюю реализацию, а не поведение. При использовании Enzyme каждый рефакторинг ломает тесты, тормозя разработку. React Testing Library (RTL) решает эту проблему: тесты фокусируются на том, что видит пользователь, и не ломаются при изменении внутренней логики. Переход с Enzyme на RTL сокращает время поддержки тестов в 2–3 раза, а экономия бюджета команды на регрессиях достигает 40 %. Мы поможем настроить тестирование под ключ: от конфигурации окружения до внедрения в CI. Наши инженеры имеют 10+ лет опыта и сертификаты по React и TypeScript. React Testing Library рекомендована сообществом как стандарт. Свяжитесь с нами, чтобы начать внедрение.

Зачем React Testing Library, а не Enzyme?

Критерий Enzyme RTL
Акцент на пользователе Нет Да
Доступ к state и props Полный Нет
Устойчивость к рефакторингу Низкая Высокая
Время выполнения тестов Медленнее Быстрее на 30–40%
Простота поддержки Хрупкие тесты Стабильные тесты

Enzyme даёт доступ к state, props и внутренним методам — это удобно на короткой дистанции, но приводит к хрупкости. RTL использует query-функции, ориентированные на роли, текст и метки, как это делают пользователи. Тесты на RTL не ломаются при рефакторинге, а регрессии практически исключены.

Как настроить тестовое окружение?

Установите Vitest, jsdom, RTL, user-event и MSW. Следуйте документации Vitest для настройки. Конфигурация минимальна:

npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event vitest jsdom msw

В vitest.config.ts укажите jsdom, глобальные переменные и setup-файл. Ниже пример с порогами покрытия:

import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    environment: 'jsdom',
    setupFiles: ['./src/test/setup.ts'],
    globals: true,
    coverage: {
      provider: 'v8',
      reporter: ['text', 'lcov', 'html'],
      thresholds: { statements: 80, branches: 75, functions: 80, lines: 80 },
    },
  },
});

В setup.ts добавьте импорт @testing-library/jest-dom и настройку MSW.

Какие проблемы решает кастомный render?

Компоненты зависят от контекста: роутер, стор, query-клиент. Чтобы не оборачивать каждый тест вручную, создайте кастомный render с MemoryRouter и QueryClientProvider:

import { render, RenderOptions } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MemoryRouter } from 'react-router-dom';
import { ReactNode } from 'react';

function createTestQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });
}

export function renderWithProviders(
  ui: React.ReactElement,
  options?: RenderOptions & { initialEntries?: string[] }
) {
  const { initialEntries = ['/'], ...rest } = options ?? {};
  const queryClient = createTestQueryClient();

  function Wrapper({ children }: { children: ReactNode }) {
    return (
      <QueryClientProvider client={queryClient}>
        <MemoryRouter initialEntries={initialEntries}>{children}</MemoryRouter>
      </QueryClientProvider>
    );
  }

  return render(ui, { wrapper: Wrapper, ...rest });
}

Такой подход сокращает дублирование кода на 40% и делает тесты единообразными. Без кастомного render каждый тест содержал бы 5–10 строк обёрток — теперь это одна строка.

Как тестировать асинхронные операции?

Используйте findBy для ожидания появления элемента и MSW для мока запросов. Пример формы авторизации:

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

describe('LoginForm', () => {
  it('отображает поля и кнопку', () => {
    render(<LoginForm onSubmit={vi.fn()} />);
    expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
    expect(screen.getByRole('button', { name: /войти/i })).toBeInTheDocument();
  });

  it('вызывает onSubmit с данными', async () => {
    const user = userEvent.setup();
    const handleSubmit = vi.fn();
    render(<LoginForm onSubmit={handleSubmit} />);

    await user.type(screen.getByLabelText(/email/i), '[email protected]');
    await user.type(screen.getByLabelText(/пароль/i), 'password123');
    await user.click(screen.getByRole('button', { name: /войти/i }));

    expect(handleSubmit).toHaveBeenCalledWith({ email: '[email protected]', password: 'password123' });
  });

  it('показывает ошибку при пустом email', async () => {
    const user = userEvent.setup();
    const onSubmit = vi.fn();
    render(<LoginForm onSubmit={onSubmit} />);

    await user.click(screen.getByRole('button', { name: /войти/i }));
    expect(screen.getByText(/введите email/i)).toBeInTheDocument();
    expect(onSubmit).not.toHaveBeenCalled();
  });
});

Для асинхронной загрузки с MSW:

import { renderWithProviders } from '@/test/render';
import { screen } from '@testing-library/react';
import { server } from '@/test/server';
import { http, HttpResponse } from 'msw';
import { UserProfile } from './UserProfile';

it('загружает данные пользователя', async () => {
  renderWithProviders(<UserProfile userId="42" />);
  expect(await screen.findByText('Test User')).toBeInTheDocument();
});

it('показывает ошибку при недоступном API', async () => {
  server.use(http.get('/api/users/:id', () => HttpResponse.json({ message: 'Server error' }, { status: 500 })));
  renderWithProviders(<UserProfile userId="42" />);
  expect(await screen.findByText(/не удалось загрузить/i)).toBeInTheDocument();
});

Типичные ошибки при тестировании React-компонентов

Ошибка Последствия Решение
Тестирование внутренней реализации Ломятся при рефакторинге Используйте RTL, тестируйте поведение
Отсутствие моков API Тесты зависят от сети Используйте MSW
Нет кастомного render Дублирование обёрток Создайте renderWithProviders

Что тестировать в первую очередь?

Покрывайте тестами: условный рендеринг, обработчики событий, валидацию форм, состояния загрузки и ошибок, интеграцию с роутингом. Не стоит тестировать CSS-классы, внутренние методы или snapshot-тесты без изменений — они ломаются при любом косметическом изменении. Экономия ресурсов команды достигает 50% за счёт сокращения регрессионных багов.

Процесс работы

  1. Анализ — выделяем ключевые компоненты и сценарии (3–5 дней).
  2. Настройка — конфигурируем окружение, пишем кастомный render и моки (1–2 дня).
  3. Написание тестов — покрываем критические пути, формы, async-операции (от 4 часов на компонент).
  4. Интеграция — подключаем coverage в CI, настраиваем пороги (1 день).
  5. Обучение — проводим code-review и консультации для вашей команды.

Сроки

Для проекта со стандартным набором компонентов (5–10 форм, список, модалки) базовое покрытие занимает от 5 до 10 дней. Сложные интеграции с MSW или Apollo Client могут добавить ещё 2–3 дня. Стоимость рассчитывается индивидуально после оценки объёма кода.

Что входит в работу

  • Настройка тестового окружения (Vitest, jsdom, MSW).
  • Кастомный render с провайдерами (Router, QueryClient).
  • Покрытие тестами ключевых компонентов (60–80% ветвлений).
  • Интеграция coverage в CI (GitHub Actions, GitLab).
  • Документация по тестированию для разработчиков.

Почему стоит внедрить тестирование с RTL?

Мы гарантируем, что тесты не сломаются при рефакторинге, покрытие будет не ниже 80%, а CI будет падать при падении тестов. Снижение затрат на регрессионное тестирование достигает 40%. Вы получите стабильный набор тестов, который экономит время команды и повышает уверенность в каждой сборке. Получите консультацию по настройке тестирования — свяжитесь с нами. Закажите оценку вашего проекта уже сегодня.

Почему юнит-тесты важны, но не панацея?

Баг, найденный юнит-тестом, стоит минуты исправления. Тот же баг в продакшене — часы инцидента, компенсации и потеря доверия. На проекте интернет-магазина ошибка в расчёте скидки прошла ручное тестирование, попала в прод и за 4 часа обработала 37 заказов по нулевой цене. Автотест на граничные случаи расчёта поймал бы её при первом же push. Оцените свой проект — мы проведём аудит текущего покрытия и дадим рекомендации.

Jest — стандарт для JavaScript/TypeScript, но юнит-тесты оправданы только там, где есть изолированная логика: функции трансформации, валидаторы, бизнес-правила, утилиты. Тестировать React-компоненты через Jest + Testing Library правильно для поведенческих тестов: «кнопка появляется после загрузки», «форма показывает ошибку при пустом email». Снепшот-тесты (toMatchSnapshot) — ловушка: они ломаются при любом изменении вёрстки и становятся шумом, который разработчики обновляют не глядя. Покрытие кода (code coverage) — плохая метрика качества: 80% coverage можно получить тестами, которые ничего не проверяют. Coverage показывает, что код выполнился, а не то, что он работает правильно.

Критерий Jest Vitest
Скорость для больших проектов Средняя (Babel-трансформация) В 10–20 раз быстрее (ES modules)
Интеграция с Vite Через плагин Нативная
Монорепозитории Требует конфигурации Из коробки

Vitest как альтернатива Jest для Vite-проектов: в 10–20 раз быстрее за счёт нативных ES modules без трансформации через Babel. Для монорепозиториев с тысячами тестов разница в скорости ощутима. Подробнее о юнит-тестировании.

Как настроить E2E тесты, которые не будут flaky?

Playwright обошёл Cypress по ключевым параметрам: нативная поддержка multi-tab, multi-origin, iframe; параллельное выполнение на уровне тестов; WebKit, Firefox, Chromium из коробки; нет iframe для приложения — тесты работают в реальном браузере.

Playwright codegen записывает действия и генерирует тест — хорошая точка старта, но сгенерированный код нужно рефакторить. Локаторы по text content хрупки: getByRole('button', { name: 'Оформить заказ' }) — устойчивее, чем locator('.btn-primary').

Page Object Model — стандарт организации E2E тестов. Каждая страница — отдельный класс с методами вместо прямых локаторов. Когда кнопка переехала из хедера в сайдбар — меняем в одном месте, не ищем по всем тестам.

Как избежать flaky тестов? Типичная проблема — flaky tests. Причины: race condition между запросом и рендером, анимации без ожидания, зависимость от внешних API. Решение: `page.waitForResponse()` вместо `page.waitForTimeout()`, мокирование внешних API через `page.route()`.
// Плохо
await page.click('#submit');
await page.waitForTimeout(2000);
await expect(page.locator('.success')).toBeVisible();

// Хорошо
await page.click('#submit');
await page.waitForResponse(resp =>
  resp.url().includes('/api/orders') && resp.status() === 201
);
await expect(page.getByRole('alert', { name: /заказ создан/i })).toBeVisible();

Наши инженеры гарантируют стабильность тестов в CI. Документация Playwright — основной инструмент на проектах с миллионами пользователей.

Нагрузочное тестирование с k6

k6 — инструмент для нагрузочного тестирования с JavaScript API. Сценарии пишутся как код, версионируются в git, запускаются в CI. Три основных сценария:

  • Spike test — резкий рост нагрузки: 0 → 1000 пользователей за 30 секунд. Имитирует запуск рекламной кампании. Показывает способность системы реагировать на пики.
  • Soak test — стабильная нагрузка на 2–4 часа. Выявляет memory leaks, connection pool exhaustion, деградацию производительности.
  • Stress test — нагрузка выше расчётной (150–200% от ожидаемого пика). Показывает точку отказа и graceful degradation.

Пороговые значения:

thresholds: {
  http_req_duration: ['p95<500', 'p99<1000'],
  http_req_failed: ['rate<0.01'],
}

p95 < 500ms означает: 95% запросов отвечают быстрее полусекунды. Если порог не выполняется — k6 завершается с кодом ошибки, CI-пайплайн падает.

На одном проекте интернет-магазина мы выявили деградацию API на 4-й час теста: p95 вырос с 200ms до 2s из-за утечки соединений. После оптимизации клиент сэкономил около $15,000 в год на инцидентах и лишних ресурсах. Получите аналогичный аудит вашего проекта — закажите нагрузочное тестирование.

Как Core Web Vitals влияют на ранжирование?

Google использует Core Web Vitals в ранжировании. Lighthouse CLI в CI-пайплайне: при каждом деплое проверяем, что LCP < 2.5s, CLS < 0.1, INP < 200ms. Подробнее о веб-производительности. Реальные проблемы, которые Lighthouse находит:

  • Hero image без width/height атрибутов: CLS 0.35 при загрузке.
  • JavaScript-бандл 2.1MB синхронно блокирует парсинг: INP 450ms.
  • Шрифты без font-display: swap: невидимый текст до загрузки шрифта (FOIT).
  • Неоптимизированный hero image 4MB: LCP 8.2s.

Lighthouse CI (lhci) сохраняет историю метрик и отправляет комментарий к PR с деградацией. По данным Google, 53% пользователей покидают сайт при загрузке дольше 3 секунд — наши тесты предотвращают такие потери.

Пирамида тестирования в проекте

Уровень Инструмент Количество Скорость
Юнит Vitest/Jest Много (тысячи) <5 мин
Интеграция Vitest + supertest Среднее 5–15 мин
E2E Playwright Немного (happy path) 10–30 мин
Нагрузка k6 По расписанию 30–60 мин
Performance Lighthouse CI При каждом деплое 5 мин

Что входит в работу?

  • Аудит текущего покрытия и определение критических user flows.
  • Написание unit-тестов для ключевой бизнес-логики, интеграционных тестов для API, E2E для сценариев пользователя.
  • Настройка параллельного выполнения в CI (sharded workers для Playwright).
  • Нагрузочное тестирование с отчётом и рекомендациями.
  • Документация по тест-кейсам, обучение вашей команды работе с тестами.
  • Гарантийная поддержка 1 месяц после внедрения.

Процесс работы

  1. Аналитика — аудит текущего тестирования, выявление слабых мест, определение приоритетов.
  2. Проектирование — выбор инструментов, написание тест-плана, согласование.
  3. Реализация — написание тестов, интеграция в CI.
  4. Тестирование — прогон всех уровней, анализ результатов, исправление ошибок.
  5. Деплой — запуск в прод, мониторинг метрик, обучение команды.

Сроки

Настройка полного тест-пайплайна (Jest + Playwright + k6 + Lighthouse CI) с нуля: 2–4 недели. Покрытие E2E-тестами существующего проекта (20–30 сценариев): 3–6 недель. Нагрузочное тестирование с отчётом и рекомендациями: 1–2 недели. Стоимость рассчитывается индивидуально после аудита.

Готовы обсудить ваш проект? Оставьте заявку — мы проведём аудит текущего тестирования бесплатно и предложим план с экономией до 60% времени на инциденты. Получите консультацию по тестированию веб-приложений — напишите нам.