Хуки (beforeEach, afterEach)

Урок: Хуки в Playwright

Введение

Представь, что ты каждый день начинаешь работу с одного и того же: включаешь компьютер, открываешь нужные программы, заходишь в рабочие сервисы.

И в конце дня делаешь примерно одно и то же: сохраняешь изменения, закрываешь приложения, выключаешь компьютер.

Теперь представь, что тебе приходится делать это вручную перед каждым действием. Это было бы неудобно и утомительно.

В тестах ситуация похожая. Часто перед каждым тестом нужно:

  • открыть страницу;
  • авторизоваться;
  • подготовить данные.

А после теста:

  • очистить состояние;
  • закрыть ресурсы.

Чтобы не писать это в каждом тесте, в Playwright существуют хуки.

Хуки — это специальные функции, которые выполняются до или после тестов автоматически.

Официальная справка по API: test (beforeEach / afterEach / beforeAll / afterAll). Как хуки сочетаются с воркерами и параллельным запуском — в разделе Parallelism.


Что такое хуки

Хуки — это функции, которые выполняются в определённые моменты жизненного цикла теста.

В Playwright есть четыре основных хука:

  • test.beforeAll — один раз на процесс воркера перед всеми тестами в области, к которой привязан хук;
  • test.afterAll — один раз на воркер после всех этих тестов;
  • test.beforeEach — перед каждым тестом;
  • test.afterEach — после каждого теста.

Если зарегистрировано несколько хуков одного типа, они выполняются в порядке регистрации. У каждого хука может быть необязательное имя (первая строка-аргумент) — так проще читать отчёты и трейсы.

Пример:

import { test } from '@playwright/test';

test.beforeEach('Стартовая страница', async ({ page }) => {
  await page.goto('https://example.com');
});

test('пример', async ({ page }) => {
  await page.getByRole('button', { name: 'Submit' }).click();
});

Здесь:

  • перед каждым тестом автоматически открывается страница;
  • сам тест становится короче и чище.

beforeEach — выполняется перед каждым тестом

Это самый часто используемый хук.

test.beforeEach('Стартовая страница', async ({ page }) => {
  await page.goto('https://example.com');
});

Что происходит:

  • перед каждым тестом открывается страница;
  • не нужно дублировать этот код в каждом тесте.

Это удобно, когда:

  • есть общий стартовый сценарий;
  • нужно одинаковое состояние перед тестами.

afterEach — выполняется после каждого теста

Этот хук используется для очистки или логирования.

test.afterEach(async ({ page }) => {
  console.log('Тест завершён');
});

Можно использовать для:

  • очистки данных;
  • логирования;
  • сбора информации о тесте.

beforeAll — один раз на воркер перед тестами в группе

test.beforeAll(async () => {
  console.log('Запуск тестового набора');
});

Важно из документации Playwright: beforeAll / afterAll выполняются один раз на worker process для тестов в своей области (файл или test.describe), а не «строго один раз на всю матрицу CI». При параллельных тестах в одном файле каждый тест может оказаться в своём воркере — тогда beforeAll отработает для каждого такого воркера отдельно.

Если воркер перезапускается (например, после падения теста с ретраями), хуки beforeAll / afterAll в новом воркере выполняются снова — см. workers and failures.

Типичные сценарии:

  • тяжёлая подготовка, которую не хочется повторять перед каждым тестом (с осторожностью при параллелизме);
  • общий ресурс в рамках одного воркера.

afterAll — один раз на воркер после тестов в группе

test.afterAll(async () => {
  console.log('Все тесты завершены');
});

Используется для:

  • освобождения ресурсов, созданных в beforeAll;
  • завершения процессов;
  • финального логирования.

Порядок нескольких afterAll — тоже по регистрации.


Область хуков: test.describe

Хуки, объявленные внутри test.describe('…', () => { … }), действуют только на тесты этой группы. Так можно разделить разный сетап для разных наборов кейсов — см. примеры с группами в Annotations.

test.describe('Корзина', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/cart');
  });

  test('пустая корзина', async ({ page }) => {
    // ...
  });
});

Параллельные тесты и общее состояние

Если в файле включён параллельный режим (test.describe.configure({ mode: 'parallel' }) или fullyParallel в конфиге), тесты из одного файла могут идти в разных воркерах. Тогда нельзя безопасно полагаться на общую глобальную переменную между тестами — каждый тест получает свой набор хуков и фикстур. Для зависимых друг от друга тестов в документации описан режим serial и пример с page, созданным в beforeAll — но его рекомендуют избегать, если можно сделать тесты изолированными.


test.info() в хуках

Внутри хука удобно смотреть метаданные текущего прогона:

test.afterEach(async ({ page }) => {
  const info = test.info();
  console.log(`Завершён тест: ${info.title}, статус: ${info.status}`);
});

Также доступны workerIndex, parallelIndex и др. — см. TestInfo.


Таймауты из хуков

Таймаут теста общий с телом теста и с beforeEach / afterEach. При необходимости его можно увеличить из хука:

test.beforeEach(async ({ page }, testInfo) => {
  await page.goto('/app');
  test.setTimeout(testInfo.timeout + 30_000);
});

Для beforeAll / afterAll таймаут задаётся отдельно для самого хука: test.setTimeout(60_000) внутри хука — см. test.setTimeout. В beforeAll / afterAll нельзя вызывать test.slow() — вместо этого вызывают test.setTimeout(...).


Пример полного сценария с хуками

import { test, expect } from '@playwright/test';

test.beforeEach(async ({ page }) => {
  await page.goto('https://example.com/login');
});

test('логин', async ({ page }) => {
  await page.getByLabel('Email').fill('test@mail.com');
  await page.getByLabel('Пароль').fill('1234');
  await page.getByRole('button', { name: 'Войти' }).click();

  await expect(page.getByText('Добро пожаловать')).toBeVisible();
});

test('ошибка логина', async ({ page }) => {
  await page.getByLabel('Email').fill('wrong@mail.com');
  await page.getByLabel('Пароль').fill('wrong');
  await page.getByRole('button', { name: 'Войти' }).click();

  await expect(page.getByText('Ошибка')).toBeVisible();
});

Что здесь важно:

  • beforeEach убирает дублирование;
  • тесты становятся чище;
  • логика сосредоточена в одном месте.

Почему хуки важны

Без хуков тесты выглядят так:

await page.goto('/login');
await page.goto('/login');
await page.goto('/login');

Повторение кода:

  • усложняет поддержку;
  • увеличивает вероятность ошибок;
  • делает тесты длиннее.

С хуками:

  • код централизован;
  • изменения вносятся в одном месте;
  • тесты проще читать.

Когда использовать разные хуки

Важно понимать разницу:

  • beforeEach — когда нужно одинаковое состояние перед каждым тестом;
  • beforeAll — когда достаточно выполнить действие один раз;
  • afterEach — когда нужно очистить после каждого теста;
  • afterAll — когда нужно завершить всё в конце.

Например:

  • авторизация → beforeEach;
  • запуск сервера → beforeAll.

Частые ошибки

Ошибка 1 — слишком много логики в хуках

test.beforeEach(async ({ page }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill('test');
  await page.getByLabel('Пароль').fill('1234');
  await page.getByRole('button', { name: 'Войти' }).click();
});

Иногда это делает тесты менее гибкими.

Ошибка 2 — скрытая логика

Когда тест не показывает, что происходит, потому что всё спрятано в хуках.

Важно сохранять баланс.


Где это используется на практике

Хуки используются почти в каждом проекте:

  • подготовка данных;
  • авторизация;
  • открытие страниц;
  • очистка состояния;
  • логирование.

Особенно важны:

  • в больших тестовых наборах;
  • при CI/CD;
  • при параллельных тестах.

Итоговое понимание

Хуки — это способ автоматизировать повторяющиеся действия вокруг тестов.

Ключевая идея в том, что они позволяют:

  • убрать дублирование;
  • сделать тесты чище;
  • централизовать логику.

С точки зрения Playwright важно помнить: beforeAll / afterAll привязаны к воркеру, порядок нескольких хуков — по регистрации, область задаётся describe, а при параллельном запуске нужно не делить глобальное состояние между тестами без явной изоляции.

Хорошее использование хуков делает тесты понятными, короткими и удобными для поддержки. Хуки должны помогать читать тест, а не скрывать его смысл — подробности и краевые случаи см. в документации test и параллелизме.