Перейти к содержимому
# testing

Тестирование VKUI-приложений

Инструментов для тестирования React-приложений несколько, но для функционального и unit-тестирования VKUI мы рекомендуем Jest и React Testing Library. Этот же стек мы применяем для тестирования компонентов VKUI. Здесь разберём основные подходы.

Специфичная настройка Jest для VKUI-компонентов не требуется, но возможны трудности с компонентами, которые опираются на браузерное API — об этом в секции ниже.

Для корректной работы VKUI-компонентов нужна обязательная обёртка, о чём говорится в Быстром старте.

Чтобы тесты было проще поддерживать, не завязывайтесь на детали реализации. Не опираясь на внутреннее устройство, вы делаете тесты устойчивыми к изменениям структуры VKUI — даже в рамках минорных версий.

Мы стараемся снабжать все компоненты функциональными ролями и атрибутами или обеспечивать их поддержку. Вместе с React Testing Library, которая работает с настоящим DOM, вы взаимодействуете с компонентами почти как пользователь.

Например, Checkbox по умолчанию имеет role="checkbox", поэтому найти его можно через API React Testing Library:

import { render, screen } from '@testing-library/react';
 
test('best practices of element accesing', () => {
  // ✅ - используем функциональную роль
  render(<Checkbox>Text</Checkbox>);
  const checkbox = screen.getByRole('checkbox');
 
  // ❌ - завязываемся на класс, который может измениться
  render(<Checkbox>Text</Checkbox>);
  const checkbox = document.querySelector('.CheckboxInput__input');
 
  // write your test...
});

Обратите внимание: если компонент — композиция более мелких, не всегда получится опираться на роль или атрибут. В таком случае для нужной части можно прокинуть data-testid. Например:

import { render, screen } from '@testing-library/react';
 
test('check if prevButton disabled', () => {
  render(<Pagination prevButtonTestId="prevButton" currentPage={1} totalPages={10} />);
 
  const prevButton = screen.getByTestId('prevButton');
  expect(prevButton).toBeDisabled();
});

Если всё же не получается протестировать нужную часть без привязки, например, к внутреннему классу, создайте feature request ↗ на GitHub.

Некоторые компоненты зависят от браузерного API, которого нет в окружении Jest. Для их тестирования предоставьте mock-реализацию самостоятельно. Подробнее — в руководстве по manual mocks ↗.

Так как VKUI мимикрирует под разные платформы, некоторые компоненты существенно отличаются структурой. Например, Select на мобильных платформах (android/iOS) — нативный элемент, а на десктопе — кастомная реализация.

Из-за поддержки SSR мы не всегда заранее знаем, какое представление отрисовывать. Если отрендерить Select без указания платформы, в DOM окажутся две реализации. Чтобы избежать ошибок, оберните Select в PlatformProvider с vkcom для web-реализации или android для мобильной.

import { render } from '@testing-library/react';
import { PlatformProvider, Select } from '@vkontakte/vkui';
 
test('test web view', () => {
  render(
    <PlatformProvider value="vkcom">
      <Select id="select-id" options={[]} {...props} />
    </PlatformProvider>,
  );
 
  // write your test...
});
 
test('test mobile view', () => {
  render(
    <PlatformProvider value="android">
      <Select id="select-id" options={[]} {...props} />
    </PlatformProvider>,
  );
 
  // write your test...
});

Для тестирования мобильного представления используйте стандартное API React Testing Library и Testing Library:

import { render, screen } from '@testing-library/react';
import { userEvent } from './testing/helpers';
import { PlatformProvider, Select } from '@vkontakte/vkui';
 
test('test is value selected on mobile', () => {
  render(
    <PlatformProvider value="android">
      <Select
        data-testid="target"
        defaultValue="1"
        options={[
          { value: '0', label: 'Mike' },
          { value: '1', label: 'Josh' },
        ]}
      />
    </PlatformProvider>,
  );
 
  await userEvent.selectOptions(screen.getByTestId('target'), ['0']);
  expect(screen.getByTestId('target')).toHaveValue('0');
});

Для тестирования web-реализации используйте более низкоуровневое API React Testing Library:

import { render, fireEvent, screen } from '@testing-library/react';
import { PlatformProvider, Select } from '@vkontakte/vkui';
 
test('test is value selected on web', () => {
  render(
    <PlatformProvider value="vkcom">
      <Select
        labelTextTestId="labelTextTestId"
        defaultValue="0"
        options={[
          { value: '0', label: 'Mike' },
          { value: '1', label: 'Josh' },
        ]}
      />
    </PlatformProvider>,
  );
 
  fireEvent.click(screen.getByTestId('labelTextTestId'));
  const unselectedOption = screen.getByRole('option', { selected: false, name: 'Josh' });
  fireEvent.mouseEnter(unselectedOption);
  fireEvent.click(unselectedOption);
 
  expect(screen.getByTestId('labelTextTestId').textContent).toEqual('Josh');
});