Integration тесты

Integration тесты (интеграционные тесты) проверяют корректное взаимодействие нескольких компонентов или модулей приложения, включая UI-библиотеки, такие как MUI (Material-UI). В отличие от unit-тестов, которые изолируют отдельный компонент, интеграционные тесты оценивают поведение компонентов в реальном контексте приложения: рендеринг, обработку событий и взаимодействие с состоянием.

В MUI важную роль играют компоненты с внутренней логикой, такие как Autocomplete, DatePicker, Drawer или DataGrid. Эти компоненты требуют тестирования их интеграции с состоянием приложения и внешними библиотеками (например, react-hook-form или formik).


Настройка среды для интеграционных тестов

Для интеграционных тестов с MUI чаще всего используют комбинацию:

  • @testing-library/react — для рендеринга компонентов и симуляции действий пользователя.
  • jest — как тестовый раннер и фреймворк для утверждений.
  • msw (Mock Service Worker) — для мокирования API-запросов, если компоненты взаимодействуют с сервером.

Пример базовой настройки:

import { render, screen, fireEvent } from '@testing-library/react';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import MyComponent from './MyComponent';

const theme = createTheme();

const renderWithTheme = (ui) => {
  return render(<ThemeProvider theme={theme}>{ui}</ThemeProvider>);
};

Важно: MUI-компоненты зависят от темы, поэтому интеграционные тесты должны оборачивать компоненты в ThemeProvider. Без этого некоторые стили и поведение могут работать некорректно.


Тестирование интерактивных компонентов

Пример: тестирование Button с событием

import { Button } from '@mui/material';

test('клик по кнопке вызывает функцию', () => {
  const handleClick = jest.fn();
  renderWithTheme(<Button onCl ick={handleClick}>Нажми меня</Button>);

  fireEvent.click(screen.getByText('Нажми меня'));
  expect(handleClick).toHaveBeenCalledTimes(1);
});

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

  • Используется fireEvent для симуляции пользовательских действий.
  • Тест проверяет, что обработчик события корректно вызывается, что важно для интеграции с бизнес-логикой.

Пример: тестирование Autocomplete

import { Autocomplete, TextField } from '@mui/material';

const options = ['Москва', 'Санкт-Петербург', 'Казань'];

test('выбор элемента в Autocomplete', () => {
  renderWithTheme(
    <Autocomplete
      options={options}
      renderInput={(params) => <TextField {...params} label="Город" />}
    />
  );

  const input = screen.getByLabelText('Город');
  fireEvent.change(input, { target: { value: 'Мо' } });

  fireEvent.click(screen.getByText('Москва'));
  expect(input.value).toBe('Москва');
});

Особенности интеграционного теста для Autocomplete:

  • Проверяется связь текстового поля и выпадающего списка.
  • Симулируется реальное взаимодействие пользователя с компонентом.
  • Подтверждается, что выбранное значение корректно обновляет состояние поля.

Тестирование компонентов с асинхронными операциями

MUI-компоненты часто используют Lazy-loading, API-запросы или debounce. В таких случаях интеграционные тесты должны учитывать асинхронность.

import { waitFor } from '@testing-library/react';
import { fetchData } from './api';

jest.mock('./api');

test('DataGrid отображает данные после загрузки', async () => {
  fetchData.mockResolvedValue([{ id: 1, name: 'Item 1' }]);

  renderWithTheme(<MyDataGridComponent />);

  await waitFor(() => {
    expect(screen.getByText('Item 1')).toBeInTheDocument();
  });
});

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

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

Тестирование компонентов с формами

Комбинация MUI и react-hook-form или formik требует проверки интеграции поля ввода ↔︎ форма ↔︎ валидация.

import { TextField, Button } from '@mui/material';
import { useForm, Controller } from 'react-hook-form';

test('форма с TextField корректно валидирует', async () => {
  const onSub mit = jest.fn();
  renderWithTheme(
    <Form onSub mit={onSubmit}>
      <Controller
        name="email"
        control={control}
        rules={{ required: 'Обязательное поле' }}
        render={({ field }) => <TextField {...field} label="Email" />}
      />
      <Button type="submit">Отправить</Button>
    </Form>
  );

  fireEvent.click(screen.getByText('Отправить'));
  await waitFor(() => {
    expect(screen.getByText('Обязательное поле')).toBeInTheDocument();
  });
});

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

  • Проверяется взаимодействие MUI TextField и механизма валидации формы.
  • Убеждаются, что ошибки отображаются корректно, а данные формы передаются в onSubmit.

Рекомендации при написании интеграционных тестов MUI

  1. Оборачивать компоненты в ThemeProvider — большинство MUI-компонентов зависят от темы.
  2. Использовать Testing Library для симуляции пользовательских действий, а не прямого вызова методов.
  3. Мокировать сетевые запросы с помощью msw или jest.mock.
  4. Проверять состояние UI после событий, а не только вызовы функций.
  5. Асинхронные действия оборачивать в waitFor для корректной проверки DOM.
  6. Стараться покрывать сложные компоненты полностью, включая внутренние подкомпоненты и пользовательские события.
  7. Следить за accessibility — интеграционные тесты с MUI должны использовать селекторы getByRole и getByLabelText для стабильности и соответствия ARIA.

Особенности тестирования специфичных компонентов MUI

  • Dialog и Modal: убедиться, что open и close работают корректно, кнопки внутри модального окна вызывают соответствующие функции.
  • Snackbar: проверка появления и автоматического скрытия после таймаута.
  • Drawer: тестирование анимаций открытия/закрытия и кликов по бэкдропу.
  • DatePicker: проверка правильного выбора даты, взаимодействия с календарем и форматирования значения.

Интеграционные тесты в MUI позволяют гарантировать надежность приложения, проверяя не только отдельные компоненты, но и их совместную работу в реальных сценариях. Такой подход уменьшает риск ошибок при обновлении UI-библиотеки или бизнес-логики.