# testing

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

Несмотря на то, что существует несколько различных инструментов по тестированию React-приложений, мы рекомендуем использовать Jest и React Testing Library для функционального или unit-тестирования VKUI-приложений. В данном руководстве мы рассмотрим основные подходы к тестированию, используя данный стек технологий. Его же мы применяем сами для тестирования компонентов VKUI.

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

Для корректной работы VKUI-компонентов необходимо использовать обязательную обёртку, об этом упоминается в Быстром старте.

Для того, чтобы тесты было проще поддерживать, рекомендуется избегать завязки на детали имплементации компонентов. Не полагаясь на внутреннее устройство, вы делаете свои тесты более устойчивыми к изменениям структуры в 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-реализацию самостоятельно. Подробнее читайте в этом руководстве ↗.

Так как 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');
});