Оглавление
Долгое время я не верил в автоматическое UI-тестирование. Казалось: «Проще потыкать руками». Пока однажды, после очередного релиза, не потекло всё — и кнопка «Купить», и модалка, и вообще всё, что могло.
Так я начал автоматизировать UI-тесты. Не ради хайпа. Ради выживания.
Почему я пришёл к автоматизации
Мы разрабатывали крупный сервис: дэшборды, таблицы, модалки, формы. Я каждый раз перед релизом:
— руками кликал по десяткам экранов,
— открывал devtools и чекал, что API не падает,
— сравнивал скрины в Figma и в браузере.
На это уходили часы. А баги всё равно пролетали.
Типовой сценарий:
Менеджер: «Кнопка не работает».
Я: «Какая?»
Менеджер: «Вот эта, “Опубликовать”».
Я: «На staging проверял, было ок».
Менеджер: «На проде — не ок».
Нужна была страховка. Надёжная. Повторяемая. Автоматическая.
Что именно я автоматизировал
Я разбил тесты на три уровня:
1. Компонентные тесты
Проверяют поведение одного компонента в отрыве от всего приложения. Использую:
— @testing-library/react
— для симуляции действий пользователя;
— Jest — как раннер (или Vitest, если проект на Vite);
— MSW — для подмены сетевых запросов в изоляции.
Пример:
it('отправляет форму при заполнении всех полей', async () => {
render( );
await user.type(screen.getByLabelText(/Имя/i), 'Влад');
await user.type(screen.getByLabelText(/Email/i), 'test@mail.ru');
user.click(screen.getByRole('button', { name: /Отправить/i }));
expect(await screen.findByText(/Спасибо!/i)).toBeVisible();
});
2. Визуальные тесты
Проверяют внешний вид компонента. Использую:
— Storybook для рендеринга всех состояний компонентов;
— Loki — для скриншотных тестов;
— GitHub Actions + Docker — чтобы всё гонялось на CI.
Добавляю сценарии вроде:
— <Button>
в трёх размерах,
— <Modal>
в открытом виде,
— <Input>
с ошибкой валидации.
Так я ловлю пиксельные баги до дизайнера.
3. End-to-end тесты (e2e)
Проверяют путь пользователя в браузере, как будто он живой. Использую:
— Playwright — мощный, быстрый, надёжный;
— запускаю в headless-режиме через CI;
— тестирую только ключевые сценарии.
test('пользователь может залогиниться и увидеть дашборд', async ({ page }) => {
await page.goto('/login');
await page.fill('input[name="email"]', 'user@mail.com');
await page.fill('input[name="password"]', 'qwerty123');
await page.click('button:has-text("Войти")');
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('h1')).toContainText('Добро пожаловать');
});
Как строится пайплайн тестирования
Вот структура проекта:
src/
components/
Button/
Button.tsx
Button.test.tsx
Button.stories.tsx
pages/
LoginPage/
LoginPage.tsx
LoginPage.e2e.ts
.storybook/
config.js
tests/
e2e/
login.spec.ts
CI прогоняет всё в три шага:
1. Компонентные тесты — юнит + интеракции.
2. Loki делает скриншоты и сравнивает с предыдущими.
3. Playwright — проходит по сценариям e2e.
Если хоть один шаг падает — PR блокируется. Это и дисциплина, и спокойствие: ничего не ускользнёт.
До и после автоматизации
До:
— релизы — боль,
— тестирование — кликодром,
— баги — на каждом шаге.
После:
— тесты пишутся вместе с кодом,
— ошибки ловятся до ревью,
— релиз в пятницу — не катастрофа.
Советы, которые сработали у меня
🟢 Пишите тесты в тот же день, что и компонент. Потом будет лень и не до того.
🟢 Не тестируйте реализацию. Тестируйте поведение. Не expect(wrapper.state().isOpen).toBe(true)
, а expect(modal).toBeVisible()
.
🟢 Снимайте снапшоты компонентов только в Storybook. Не через toMatchSnapshot()
— он шумит, и никто не читает дельты.
🟢 Старайтесь, чтобы e2e тест проверял только одну бизнес-ценность. Например, «Пользователь добавил товар в корзину».
🟢 Каждый баг — это будущий тест. Упало на проде? Напиши тест, чтобы не повторилось.
Ошибки, которые я допускал
🔴 Пытался покрыть тестами всё подряд. Это не нужно. Бессмысленно писать e2e на каждый экран. Нужно покрывать только важные пути.
🔴 Делал тесты зависимыми от бэка. Если API упал, тест падал — и неясно, баг ли это. Сейчас всё мокаю через MSW и playwright fixtures.
🔴 Писал тесты без обоснования. Сначала думал: «Нужно 100% coverage». А теперь думаю: «Этот тест помогает мне спать спокойно?».
Лайфхаки, которые упростили жизнь
🛠 Playwright fixtures. Позволяют запускать тесты с нужным состоянием (авторизованный пользователь, корзина с товарами и т.д.).
🛠 Визуальные снапшоты в PR. Loki может отправить скриншоты в комментарии к Pull Request — удобно для ревью.
🛠 Storybook тесты в отдельном CI‑джобе. Чтобы не тормозили основные проверки и запускались только при изменении UI.
🛠 Типизация моков. Через ts-mockito
или кастомные MockApiClient
— так я не боюсь обновлений API.
Что дальше
Мой стек сейчас:
— 🧪 @testing-library/react — компоненты,
— 📸 Loki — визуальные тесты,
— 🌐 Playwright — end-to-end,
— 🧱 Storybook — документация и основа для тестов.
Покрыто 70% ключевых сценариев. Багов меньше. Разработка — спокойнее. А главное — можно доверять коду, даже если ты в отпуске.
Вывод
Автоматизация тестов — это не про 100% покрытие. Это про уверенность в коде.
Начните с малого:
— Напишите 1 e2e тест.
— Добавьте Storybook.
— Прикрутите визуальный тест к кнопке.
А дальше — втянетесь. Проверено на себе.
Комментарии: