Владислав Калачев

Как я автоматизировал тестирование UI: опыт и лучшие практики

11

Долгое время я не верил в автоматическое 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.
— Прикрутите визуальный тест к кнопке.

А дальше — втянетесь. Проверено на себе.

Комментарии:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *