В браузере тесты изолированы, но данные — внешнее состояние, и тут изоляция не бесплатна. Два теста, работающие с одной записью в базе, мешают друг другу; вход через UI в каждом тесте крадёт минуты. Аккуратная работа с данными и аутентификацией — то, что отличает стабильный e2e-набор от медленного и флакающего.
Готовить данные через API, а не UI
Соблазн — создавать нужное состояние через интерфейс: чтобы протестировать корзину, сначала кликами завести товар, зарегистрировать продавца и так далее. Это медленно и хрупко: тест корзины падает из-за бага в форме создания товара, к корзине отношения не имеющего.
Правильно — готовить данные кратчайшим путём, обычно через API:
test("в корзине виден добавленный товар", async ({ page, request }) => {
const res = await request.post("/api/products", {
data: { name: "Кофемолка", price: 4990 },
});
const product = await res.json();
await page.goto(`/products/${product.id}`);
await page.getByRole("button", { name: "В корзину" }).click();
await expect(page.getByText("Кофемолка")).toBeVisible();
});
Через UI проходит только то, что тест реально проверяет (добавление в корзину); подготовка — через request (встроенная фикстура API-клиента). Быстрее и устойчивее.
Аутентификация через storageState
Логиниться через форму в каждом тесте — дорого. Playwright позволяет залогиниться один раз, сохранить состояние сессии (storageState — cookies и localStorage) и переиспользовать его.
// global setup: логин один раз
await page.goto("/login");
await page.getByLabel("Email").fill("user@example.com");
await page.getByLabel("Пароль").fill("secret");
await page.getByRole("button", { name: "Войти" }).click();
await page.context().storageState({ path: "auth/user.json" });
// playwright.config.ts — переиспользовать в project
use: { storageState: "auth/user.json" }
Тесты стартуют уже залогиненными — без формы входа в каждом. Для разных ролей готовят несколько файлов состояния и projects под них.
Setup и teardown
Подготовку и очистку выносят на нужный уровень: глобальный setup (один раз перед всем набором — например, auth-состояния), per-test (фикстуры — данные конкретного теста). Очистка важна не меньше: тест убирает за собой созданное, чтобы не копить мусор и не влиять на следующие.
Изоляция данных
Главное правило: тест не должен зависеть от данных, оставленных другим. Каждый создаёт своё (уникальные имена/идентификаторы, чтобы параллельные прогоны не сталкивались) и не полагается на «там уже есть запись». Зависимость от общего изменяемого состояния — частый источник флаки, особенно при параллельном запуске в CI.
Где это в UCP
Данные и аутентификация — про скорость и изоляцию e2e: готовить состояние через API, переиспользовать вход через storageState, чистить за собой, не зависеть от чужих данных. Это та же дисциплина изоляции и подготовки, что тестовая база в backend, только на уровне собранного продукта. Для продукт-инженера это быстрый и стабильный набор, готовый к параллельному прогону; а контроль того, что отвечает сеть, — следующая тема.