Мокирование дочерних компонентов

Мокирование дочерних компонентов — важная техника в тестировании React-приложений с помощью React Testing Library (RTL). Она позволяет изолировать тестируемый компонент, не загружая реальную логику и поведение его потомков. Такой подход упрощает тесты, делает их быстрее и сосредоточенными на функциональности конкретного компонента.

Зачем мокировать дочерние компоненты

Мокирование полезно в следующих случаях:

  • Снижение сложности теста: когда дочерний компонент содержит сложную логику или сторонние зависимости.
  • Изоляция поведения: проверка работы родителя без влияния состояния потомков.
  • Контроль над выводом: предоставление предсказуемого контента вместо динамических элементов.

Примером может служить компонент Dashboard, который использует UserProfile и Notifications. Чтобы протестировать только Dashboard, логично заменить UserProfile и Notifications на простые моки.

Мокирование с использованием Jest

Наиболее распространённый способ — это мокирование через Jest, который часто используется совместно с RTL.

import { render, screen } from '@testing-library/react';
import Dashboard from './Dashboard';

// Мокаем дочерний компонент
jest.mock('./UserProfile', () => () => <div data-testid="mocked-user-profile" />);

jest.mock('./Notifications', () => () => <div data-testid="mocked-notifications" />);

test('рендерит Dashboard с мокированными компонентами', () => {
  render(<Dashboard />);

  expect(screen.getByTestId('mocked-user-profile')).toBeInTheDocument();
  expect(screen.getByTestId('mocked-notifications')).toBeInTheDocument();
});

Ключевые моменты:

  • jest.mock() заменяет реальный модуль на функцию-заглушку.
  • Можно использовать data-testid для удобного поиска элементов в тестах.
  • Родительский компонент продолжает работать с моком как с обычным компонентом.

Передача пропсов в моки

Иногда важно проверить, какие пропсы передаются дочернему компоненту. В этом случае мокающий компонент может сохранять полученные пропсы:

const mockUserProfile = jest.fn(() => <div>Mocked UserProfile</div>);
jest.mock('./UserProfile', () => props => {
  mockUserProfile(props);
  return <div data-testid="mocked-user-profile" />;
});

test('Dashboard передаёт правильные пропсы в UserProfile', () => {
  render(<Dashboard user={{ name: 'Alice' }} />);
  
  expect(mockUserProfile).toHaveBeenCalledWith(
    expect.objectContaining({ user: { name: 'Alice' } }),
    {}
  );
});

Особенности:

  • Мок сохраняет вызовы через jest.fn().
  • expect.objectContaining позволяет проверять только нужные поля пропсов.

Частичное мокирование

Иногда требуется замокировать только часть функциональности модуля, оставив остальное нетронутым. Для этого используется jest.requireActual:

jest.mock('./Notifications', () => {
  const originalModule = jest.requireActual('./Notifications');
  return {
    __esModule: true,
    ...originalModule,
    default: () => <div data-testid="mocked-notifications" />,
  };
});

Такой подход полезен, когда компонент экспортирует несколько функций или компонентов, и требуется замокировать лишь один из них.

Мокирование компонентов с асинхронной логикой

Если дочерний компонент использует useEffect для загрузки данных, мок может быть синхронным и возвращать статический результат:

jest.mock('./AsyncComponent', () => () => <div data-testid="mocked-async">Loaded</div>);

В тесте можно сразу проверять состояние родителя, не ожидая асинхронного рендера.

Важные рекомендации

  • Мокировать следует только компоненты, которые не критичны для теста.
  • Не стоит заменять всё, иначе тест может потерять смысл и не проверять реальные сценарии.
  • Использование data-testid делает тесты проще и устойчивее к изменениям разметки.
  • При необходимости проверки взаимодействия между родителем и потомком лучше использовать частичное мокирование или jest.fn() для отслеживания вызовов.

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