Тестирование VKUI-приложений
Тестирование с помощью Jest и React Testing Library
Несмотря на то, что существует несколько различных инструментов по тестированию React-приложений,
мы рекомендуем использовать Jest и React Testing Library для функционального или unit-тестирования VKUI-приложений.
В данном руководстве мы рассмотрим основные подходы к тестированию, используя данный стек технологий.
Его же мы применяем сами для тестирования компонентов VKUI.
Jest
Специфичной настройки Jest для тестирования VKUI-компонентов не требуется, но могут возникнуть трудности
с рядом компонентов, которые опираются на браузерное API. Подробнее об этом написано в секции ниже.
Для корректной работы VKUI-компонентов необходимо использовать обязательную обёртку, об этом упоминается в Быстром старте.
React Testing Library
Для того, чтобы тесты было проще поддерживать, рекомендуется избегать завязки на детали имплементации компонентов.
Не полагаясь на внутреннее устройство, вы делаете свои тесты более устойчивыми к изменениям структуры в 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.
Особые компоненты
Snackbar, Spinner, SplitCol
Некоторые компоненты зависят от браузерного API, которое не реализовано в окружении Jest, поэтому для
тестирования подобных компонентов вам необходимо предоставить mock-реализацию самостоятельно.
Подробнее читайте в этом руководстве ↗.
Select
Так как 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');
});