Frontend-тесты ломаются чаще backend-овых — не потому, что фронт сложнее, а потому, что их часто пишут не про то. Тест, привязанный к классам, структуре разметки и внутреннему состоянию, краснеет на каждом рефакторинге, ничего не проверив по сути. Главный принцип frontend-тестирования один: проверять поведение, которое видит пользователь, а не то, как оно сделано внутри.
Инструменты
Раннер — Vitest (быстрый, совместим с экосистемой Vite, API как у Jest). Для компонентов — Testing Library: она рендерит компонент и даёт искать элементы и взаимодействовать с ними так, как это делает пользователь.
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
test("показывает товар и реагирует на открытие", async () => {
const onOpen = vi.fn();
render(<ProductCard id="1" name="Кофемолка" price={4990} onOpen={onOpen} />);
expect(screen.getByText("Кофемолка")).toBeInTheDocument();
await userEvent.click(screen.getByRole("button", { name: "Открыть" }));
expect(onOpen).toHaveBeenCalledWith("1");
});
Запрос как пользователь
Ключевая идиома Testing Library — искать элементы по роли и доступному имени (getByRole, getByLabelText, getByText), а не по классам или test-id. Это сближает тест с тем, как воспринимает страницу пользователь и скринридер — и заодно проверяет доступность.
// хрупко: привязка к реализации
container.querySelector(".btn-primary");
// устойчиво: как ищет пользователь
screen.getByRole("button", { name: "Сохранить" });
Тест по роли переживает смену классов и перестройку разметки — он завязан на поведение, а не на устройство.
Что тестировать, а что нет
Тестировать стоит поведение: компонент показал нужное по данным, форма отдала введённое и показала ошибку, клик вызвал колбэк, экран отобразил состояние загрузки и ошибки. Не стоит тестировать реализацию: внутренний useState, имена классов, порядок вызовов хуков, точную верстку. Признак хорошего теста — он не меняется при рефакторинге, не меняющем поведение.
Серверные вызовы в тестах подменяют (мок fetch/слой API или MSW), чтобы тест был быстрым и не зависел от сети — так же, как backend подменяет порты.
Пирамида frontend-тестов
Та же форма, что в backend. Снизу, широко — юнит-тесты чистой логики (хуки, утилиты, валидация zod). Посередине — компонентные тесты на Testing Library (поведение компонента/экрана с моками данных) — основная масса. Сверху, узко — сквозные (e2e) тесты в реальном браузере; их мало, они медленные, и это отдельная специализация (Playwright), а не часть этого трека.
Не гонять логику через медленный e2e и не дублировать в каждом компонентном тесте полный поток — каждый уровень проверяет своё.
Где это в UCP
Тестирование frontend — это та же дисциплина, что в backend-биндингах: тестировать поведение и контракт, а не детали реализации; пирамида с быстрой широкой базой; подмена внешнего. Testing Library добавляет ценную связку — тест «как пользователь» заодно проверяет доступность. Сервис, который так покрыт, продукт-инженер меняет без страха, получая ответ за секунды, — и это замыкает фундамент frontend-специализации: от структуры проекта до качества.