Тестирование 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');
});