Testing Library и MUI

Библиотека MUI (Material-UI) предоставляет готовые компоненты интерфейса на React, но для обеспечения качества приложения необходима полноценная проверка их работы через Testing Library. Testing Library ориентирована на тестирование компонентов так, как их воспринимает пользователь, а не на реализацию внутренней логики. Это особенно важно при работе с MUI, где компоненты часто оборачиваются в сложные контейнеры и используют стилизации и контексты.


Настройка окружения

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

npm install @testing-library/react @testing-library/jest-dom @testing-library/user-event
  • @testing-library/react — основной пакет для рендеринга компонентов.
  • @testing-library/jest-dom — расширяет Jest матчерами для удобной проверки DOM.
  • @testing-library/user-event — имитация действий пользователя (клики, ввод текста и т.д.).

Если используется MUI v5, важно оборачивать тестируемые компоненты в ThemeProvider, чтобы корректно применялись темы:

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

const theme = createTheme();

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

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

Кнопки и события

Для кнопок MUI (Button) ключевым является тестирование действий пользователя через userEvent:

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

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

  const button = screen.getByRole('button', { name: /нажми меня/i });
  await userEvent.click(button);

  expect(handleClick).toHaveBeenCalledTimes(1);
});

Особенности MUI: визуальные стили и иконки не влияют на функциональность, поэтому проверка по роли и тексту предпочтительнее.


Поля ввода и формы

MUI предоставляет компоненты TextField, Select, Checkbox и др., которые оборачивают стандартные элементы HTML. Для TextField важно использовать getByLabelText или getByRole:

test('текстовое поле обновляет значение', async () => {
  renderWithTheme(<TextField label="Имя" />);

  const input = screen.getByLabelText(/имя/i);
  await userEvent.type(input, 'Алексей');

  expect(input).toHaveValue('Алексей');
});

Для Select и Autocomplete проверка требует имитации раскрытия списка и выбора элемента:

test('sel ect позволяет выбрать элемент', async () => {
  renderWithTheme(
    <Sel ect label="Фрукты" defaultValue="">
      <MenuItem value="apple">Яблоко</MenuItem>
      <MenuItem value="orange">Апельсин</MenuItem>
    </Select>
  );

  const select = screen.getByLabelText(/фрукты/i);
  await userEvent.click(select);
  const option = screen.getByRole('option', { name: /апельсин/i });
  await userEvent.click(option);

  expect(select).toHaveTextContent(/апельсин/i);
});

Работа с контекстами и провайдерами

MUI часто использует ThemeProvider, CssBaseline, LocalizationProvider (для компонентов DatePicker и TimePicker). В тестах их необходимо учитывать, иначе могут возникнуть ошибки при рендеринге:

import AdapterDateFns fr om '@mui/lab/AdapterDateFns';
import { LocalizationProvider } fr om '@mui/lab';

const renderWithProviders = (ui) =>
  render(
    <ThemeProvider theme={theme}>
      <LocalizationProvider dateAdapter={AdapterDateFns}>
        {ui}
      </LocalizationProvider>
    </ThemeProvider>
  );

Проверка визуального состояния

MUI компоненты меняют классы и атрибуты при интеракциях (hover, focus, disabled). Testing Library позволяет проверять это через Jest-DOM:

test('кнопка отключена при условии', () => {
  renderWithTheme(<Button disabled>Отправить</Button>);

  const button = screen.getByRole('button', { name: /отправить/i });
  expect(button).toBeDisabled();
});

Для проверки видимости всплывающих элементов, таких как Tooltip:

test('tooltip отображается при наведении', async () => {
  renderWithTheme(<Tooltip title="Подсказка"><Button>Наведи</Button></Tooltip>);

  const button = screen.getByRole('button', { name: /наведи/i });
  await userEvent.hover(button);

  const tooltip = await screen.findByText(/подсказка/i);
  expect(tooltip).toBeVisible();
});

Асинхронные взаимодействия

MUI использует анимации при открытии меню, диалогов и списков. Testing Library предоставляет методы findBy* и waitFor для корректной работы с асинхронными элементами:

test('диалог открывается по кнопке', async () => {
  renderWithTheme(
    <>
      <Button onCl ick={() => setOpen(true)}>Открыть</Button>
      <Dialog open={open}><DialogTitle>Заголовок</DialogTitle></Dialog>
    </>
  );

  await userEvent.click(screen.getByRole('button', { name: /открыть/i }));
  const dialogTitle = await screen.findByText(/заголовок/i);
  expect(dialogTitle).toBeInTheDocument();
});

Общие рекомендации

  • Использовать semantic queries (getByRole, getByLabelText) вместо поиска по классу или id. MUI генерирует динамические классы, которые нестабильны.
  • Оборачивать компоненты в необходимые провайдеры (Theme, Localization, Snackbar, и т.д.).
  • Для сложных взаимодействий использовать userEvent вместо fireEvent, чтобы имитировать реальные действия пользователя.
  • Проверять DOM так, как видит его пользователь: наличие текста, состояние кнопок, видимость элементов.

Заключение по методике

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