Тестирование пользовательских событий
Тестирование пользовательских событий — симуляция взаимодействий (клик, ввод текста, выбор, drag) в unit и integration тестах через
@testing-library/user-eventилиfireEvent, и через прямые действия в E2E тестах.
Зачем нужно
Компоненты с интерактивностью (кнопки, формы, модальные окна, dropdown) нельзя полноценно проверить только рендером. Симуляция событий подтверждает что логика взаимодействия работает корректно и компонент реагирует на пользовательские действия.
Где используется
- React/Vue компоненты с кнопками, формами, вкладками, аккордеонами
- Обработчики клавиатурных событий (Enter, Escape, Tab)
- Drag and drop, scroll, resize обработчики
- Тестирование accessibility: фокус, keyboard navigation
Основной контент
userEvent — основной инструмент RTL
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Counter } from './Counter';
test('клик на кнопку увеличивает счётчик', async () => {
const user = userEvent.setup;
render(<Counter initialValue={0} />);
await user.click(screen.getByRole('button', { name: 'Увеличить' }));
await user.click(screen.getByRole('button', { name: 'Увеличить' }));
expect(screen.getByText('2')).toBeInTheDocument;
});
Ввод текста
test('вводит текст в поле поиска', async () => {
const user = userEvent.setup;
render(<SearchBar onSearch={jest.fn} />);
const input = screen.getByRole('textbox', { name: 'Поиск' });
await user.type(input, 'playwright');
expect(input).toHaveValue('playwright');
});
test('очищает поле и вводит новый текст', async () => {
const user = userEvent.setup;
render(<SearchBar />);
const input = screen.getByRole('textbox');
await user.type(input, 'old query');
await user.clear(input);
await user.type(input, 'new query');
expect(input).toHaveValue('new query');
});
Клавиатурные события
test('отправляет форму нажатием Enter', async () => {
const onSubmit = jest.fn;
const user = userEvent.setup;
render(<SearchForm onSubmit={onSubmit} />);
await user.type(screen.getByRole('textbox'), 'query{Enter}');
expect(onSubmit).toHaveBeenCalledWith('query');
});
test('закрывает модальное окно по Escape', async () => {
const user = userEvent.setup;
render(<Modal isOpen={true} />);
await user.keyboard('{Escape}');
expect(screen.queryByRole('dialog')).not.toBeInTheDocument;
});
Выбор в select
test('выбирает опцию в select', async () => {
const onChange = jest.fn;
const user = userEvent.setup;
render(
<select onChange={onChange}>
<option value="ru">Русский</option>
<option value="en">English</option>
</select>
);
await user.selectOptions(screen.getByRole('combobox'), 'en');
expect(onChange).toHaveBeenCalled();
expect((screen.getByRole('combobox') as HTMLSelectElement).value).toBe('en');
});
Hover и focus
test('показывает tooltip при наведении', async () => {
const user = userEvent.setup;
render(<TooltipButton tooltip="Подсказка" label="Info" />);
await user.hover(screen.getByRole('button'));
await screen.findByText('Подсказка');
});
test('показывает outline при фокусе', async () => {
const user = userEvent.setup;
render(<Button>Нажми</Button>);
await user.tab; // Tab устанавливает фокус
expect(screen.getByRole('button')).toHaveFocus;
});
В Playwright/Cypress
// Playwright
await page.click('[data-testid="submit"]');
await page.fill('[data-testid="email"]', 'user@example.com');
await page.keyboard.press('Enter');
await page.hover('[data-testid="info-icon"]');
// Cypress
cy.get('[data-cy="submit"]').click();
cy.get('[data-cy="email"]').type('user@example.com');
cy.get('[data-cy="select"]').select('option-value');
Частые ошибки
fireEventвместоuserEvent—fireEvent.click()не симулирует реальный flow (hover, focus, mousedown, mouseup, click);userEvent.click()делает всё правильно- Нет
awaitпередuserEvent.*— все методы асинхронны в v14+; безawaitсобытия не применяются до проверки - Тест симулирует событие без проверки результата — после каждого события должен быть
expect
Связанные темы
- _MOC Тестирование
- React Testing Library -- основы
- Testing Library -- подход
- Тестирование форм
- Тестирование рендеринга