Урок 7. Arrange — подготовка перед автотестом
Независимость тестов
Темы урока
AAA как тест-кейс в коде, что класть в Arrange и что нельзя, фабрика vs хелпер vs фикстура, beforeEach / beforeAll / afterEach, независимость и атомарность, антипаттерны общего аккаунта
Видео урока
Конспект урока
Конспект — Урок 7: Один упал — остальные красные. Как так?
Главное за урок
AAA пришёл из юнит-тестов: Arrange (сцена) → Act (шаги) → Assert (ожидаемый). Это тот же тест-кейс, только в коде: предусловия / шаги / ожидаемый результат.
Первая A нужна, чтобы Act не врал. Без своего мира тест проверяет «как получилось»: общий аккаунт, чужой навык, протухшая дата, кука с прошлого прогона.
Свой пользователь на каждый тест — через beforeEach. beforeAll с одним аккаунтом на файл связывает пачку: «у меня локально прошло».
AAA = тест-кейс в коде
Концепцию сформулировали разработчики для юнит-тестов. На собеседованиях спрашивают.
| Буква | В тест-кейсе | В автотесте |
|---|---|---|
| Arrange | Предусловия | Данные, навигация, регистрация, логин |
| Act | Шаги | click / fill / отправка — Урок 8 |
| Assert | Ожидаемый результат | expect — Урок 9 |
booking-flow из прошлых уроков — учебный флоу «пройти путь глазами». Для независимости удобнее несколько коротких test() с одной мыслью каждый.
История с эфира: один стендовый аккаунт на двух локалях. Каждый тест один — зелёный. Друг за другом — красный: первая локаль прибила куку, вторая не логинилась. Виновата не «флаковость Playwright», а общая сцена.
Что класть в Arrange — и что нельзя
Arrange готовит сцену до проверяемого действия.
Можно
| Тип | Примеры на PomidorQA |
|---|---|
| Данные | Имя, email, пароль, дата слота — из фабрики |
| Навигация | goto на регистрацию, затем на /pomidorqa/profile |
| Предусловия | Зарегистрирован, залогинен, навыков ещё нет |
| Контекст браузера | Свой page на тест (Playwright даёт сам) |
Данные должны переживать повторный прогон: уникальный email, живая дата (Date.now() + смещение), а не user@test.com и не 2026-08-17.
Пароль в учебном стенде можно оставить статичным (testpass123): на регистрацию он не претендует. Email — нет: второй прогон с тем же адресом не зарегистрируется.
Нельзя
| Что | Почему |
|---|---|
| Само проверяемое действие | Хук добавил навык — тест «навык добавился» проверяет хук |
expect про фичу |
Даже «очень хочется сразу»: это Assert |
| Чужие / вечные данные | «В каталоге уже кто-то с Playwright» |
| Контракт на порядок тестов | Тест B живёт, только если A уже отработал |
Общий аккаунт в beforeAll |
Первый тест пачкает профиль — второй красный |
Регистрация в хелпере — серая зона. Если тест про регистрацию, шаги остаются в Act. Если тест про профиль после регистрации — регистрация это предусловие, её выносят. expect URL после регистрации в хелпере на курсе пока оставляем как дымовую страховку; по-хорошему проверка регистрации живёт в отдельном тесте.
UI-регистрация в каждом тесте медленная. На проде её часто уносят на API-слой (дернули ручку — получили пользователя, браузер не открывали). Пока у нас UI — нормально для курса. Суть та же: подготовка не должна быть тем, что вы утверждаете.
Фабрики, хелперы, датасеты
Три роли, которые на эфире путали.
Фабрика — на вход параметры, на выход готовый объект. Ничего не кликает.
function makeUser(role: string, runId: number): TestUser {
return {
name: `${role} Автотест`,
email: `${role}-${runId}@example.com`,
password: "testpass123",
};
}
Какие бывают:
| Вид | Что возвращает | Пример |
|---|---|---|
| Фабрика данных | Объект тестовых данных | makeUser, позже makeSlot с датой «завтра» |
| Фабрика локаторов | Locator | (page) => page.getByLabel("Имя") |
| Фабрика сущности через API | Созданный на бэке объект | Регистрация ручкой, не формой — позже |
Хелпер — выполняет действие. registerUser(page, user) не создаёт объект пользователя (это сделала фабрика), он проводит его по UI.
Датасет — набор готовых данных в отдельной папке/файле (JSON, фикстуры данных). Удобно, когда вариаций много. Мина та же, что у хардкода: статичный email и протухшая дата. Датасеты с уникальными полями из фабрики — ок; вечный qa@test.com на весь набор — нет.
Хардкод не запрещён навсегда. Запрещено хардкодить то, что должно быть уникальным между прогонами и параллельными воркерами.
Фикстуры: хук vs { page }
На эфире «фикстурой» называли подготовку в beforeEach. В Playwright слово fixture ещё и официальное.
{ page } в сигнатуре теста — это встроенная фикстура: Playwright сам открыл браузер, отдал страницу, после теста закрыл. Вы её не создаёте руками.
Хук (beforeEach) |
Фикстура Playwright | |
|---|---|---|
| Как выглядит | test.beforeEach(async ({ page }) => { ... }) |
async ({ page, user }) => { ... } |
| Что даёт | Побочный эффект «сделай до теста» | Значение, которое впрыскивают в тест |
| Уборка | afterEach |
Код после use(...) — сам |
| Когда | Учебный минимум, ДЗ-7 | Свои фикстуры — когда один и тот же Arrange копируют в десять файлов |
Встроенные фикстуры, которые уже есть: page, context, browser, request.
Своя фикстура (знать, не писать в ДЗ-7):
import { test as base } from "@playwright/test";
export const test = base.extend<{ user: TestUser }>({
user: async ({ page }, use) => {
const user = makeUser("student", Date.now());
await registerUser(page, user);
await use(user);
// сюда — teardown: удалить аккаунт, если появится ручка
},
});
Два скоупа:
| Скоуп | Когда живёт | Куда класть |
|---|---|---|
test (по умолчанию) |
Один тест | Пользователь, логин, грязные данные |
worker |
На весь воркер | Тяжёлое неизменяемое: один раз поднять мок-сервер |
Правило то же, что у Arrange: в фикстуру — сцена. Не клик «Добавить навык», если тест как раз про добавление.
Хуки: beforeEach, beforeAll, afterEach
Хук — код, который раннер вызывает до или после теста. Не путать с хелпером: хелпер вы вызываете сами.
| Хук | Когда | Куда на курсе |
|---|---|---|
beforeEach |
Перед каждым test() |
Свой makeUser + регистрация + goto профиля |
beforeAll |
Один раз на файл / describe |
Осторожно. Ок для неизменяемого. Опасно для аккаунта |
afterEach |
После каждого теста | Подместись: удалить аккаунт, закрыть лишнее |
afterAll |
Один раз в конце | Редко; та же ловушка «общее состояние» |
page.close() в afterEach на курсе — учебный жест. Playwright и так изолирует page между тестами. Полезный teardown — удалить созданного пользователя, чтобы не засирать общий стенд. Ручки удаления пока нет — куратор чистит базу руками.
На одном из проектов с эфира beforeAll запретили контрактом: экспериментальные куки на весь сьют ломали чужие тесты, дебаг шёл часами. Это не «beforeAll всегда зло». На собеседовании лучше сказать: опасен для мутабельных данных и скрытых прекондишенов на тысяче строк, уместен для общего неизменяемого сетапа.
Независимость и атомарность
Независимость. Тест можно запустить один — зелёный. Порядок в файле не контракт. Общий пользователь, общий слот, «сначала тот тест» — связь, не экономия.
Классика с эфира: тест A добавляет навык, тест B ждёт пустой can-help-skills. B один — зелёный. A+B — красный.
Атомарность. Один test() — одна мысль: имя на месте или навыков нет или слотов нет. Простыня на 15 шагов: упал шаг 3 — хвост даже не бежит, причина тонет.
Антипаттерны Arrange
- Общий аккаунт и порядок как контракт.
beforeAll+ одинregisterUserна файл. - Arrange спрятан внутри Act. Регистрация и
gotoпосреди кликов — через неделю не видно, где сцена, где шаг. - Хук уже делает Act.
beforeEachжмёт «Добавить навык», тест проверяет чип. - Хардкод уникального. Email, дата слота, имя хоста из каталога.
- Простыня. Один огромный тест вместо трёх коротких миров.
Пример структуры нового файла автотеста
Новый spec — не простыня booking-flow. Каркас: типы → фабрика → хелпер → describe → хук Arrange → короткие тесты.
ДЗ — новый файл tests/e2e/arrange-practice.spec.ts. booking-flow не трогаем. Ниже — схема, не готовое решение.
import { test, expect, type Page } from "@playwright/test";
type TestUser = {
// поля тестового пользователя
};
function makeUser(/* роль, уникальный id */): TestUser {
// фабрика: вернуть уникальные данные, без хардкода email
}
async function registerUser(page: Page, user: TestUser) {
// хелпер подготовки: регистрация
// сюда не класть проверяемое действие теста
}
test.describe("свой мир на каждый тест", () => {
let user: TestUser;
test.beforeEach(async ({ page }) => {
// Arrange:
// 1) новый пользователь из фабрики
// 2) регистрация через хелпер
// 3) переход на страницу, где будет проверка
});
test("мир 1: ...", async ({ page }) => {
// Assert: одна мысль про сцену
});
test("мир 2: ...", async ({ page }) => {
// Assert: другая мысль на той же сцене
});
});
Прогони тесты по одному и пачкой. Порядок не должен влиять.
Как подойти к домашке
- Ветка
hw7-<github-username>.booking-flow.spec.tsне трогать. - Новый файл
tests/e2e/arrange-practice.spec.tsпо схеме выше: фабрика, хелпер,beforeEach, два коротких теста. - Не хардкодь email / имя / дату. Не добавляй навык — это Act Урока 8.
- Оба теста зелёные по одному и вместе.
Ключевые тезисы для теста
- AAA — данные / действие / проверка; это предусловия / шаги / ожидаемый из тест-кейса.
- Arrange — сцена до клика. Проверяемое действие и
expectпро фичу туда не кладут. - Фабрика возвращает объект; хелпер выполняет шаг; хук вызывает подготовку до теста.
{ page }— встроенная фикстура Playwright, не «просто аргумент».beforeEach— свой мир на тест;beforeAllс мутабельным аккаунтом связывает тесты.- Хардкод email/даты даёт ложнозелёный первый прогон и красный второй.
- Один упал — остальные красные: чаще общий стейт, не баг продукта.
- Teardown (
afterEach/ код послеuseв фикстуре) возвращает окружение к чистому состоянию; закрытиеpageраннер делает сам.
Домашнее задание
Индивидуальная проверка ДЗ — на Boosty
Конспект, видео и тест открыты всем. Текст задания и проверка работы — по подписке.
Индивидуальная проверка ДЗ на Boosty