Структура тестов и хуки в Playwright

В этой главе рассказывается, как организованы тесты Playwright, включая группировку тестов, управление выполнением и использование хуков жизненного цикла.


test.describe() Группировка тестов

test.describe()Используется для объединения связанных тестов в логические группы.

Пример

// Путь к файлу: tests/describe-demo.spec.ts
import { test, expect } from '@playwright/test';

// Группировка первого уровня
test.describe('Пример теста главной страницы', () => {

  test('Заголовок страницы корректен', async ({ page }) => {
    await page.goto('https://www.example.com/');
    await expect(page).toHaveTitle(/EXAMPLE/);
  });

  test('Панель навигации существует', async ({ page }) => {
    await page.goto('https://www.example.com/');
    // Проверить, что элемент навигации существует
    await expect(page.locator('nav')).toBeVisible();
  });
});

// Можно определить несколько блоков describe
test.describe('Тест функциональности поиска', () => {

  test('Поле поиска отображается', async ({ page }) => {
    await page.goto('https://www.example.com/');
    await expect(
      page.getByPlaceholder('Search')
    ).toBeVisible();
  });
});

Отчёт о тестировании будетdescribeотображаться группами, что упрощает просмотр результатов.


Вложенные describe

test.describe()Их можно вкладывать друг в друга, образуя древовидную структуру:

Пример

test.describe('Модуль пользователя', () => {

  test.describe('Функциональность входа', () => {
    test('Вход с правильной учетной записью и паролем', async ({ page }) => { /* ... */ });
    test('Вход не выполняется при неверном пароле', async ({ page }) => { /* ... */ });
  });

  test.describe('Функциональность регистрации', () => {
    test('Регистрация с корректной информацией', async ({ page }) => { /* ... */ });
    test('Регистрация завершается ошибкой при существующем email', async ({ page }) => { /* ... */ });
  });
});

test.skip() Пропуск тестов

test.skip()Используется для пропуска теста; пропущенные тесты не выполняются.

Пример

// Пропустить напрямую (часто используется для временного отключения падающих функциональных тестов)
test.skip('Тест функциональности еще не завершен', async ({ page }) => {
  // Этот тест не будет выполняться
});

// Условный пропуск (определяется во время выполнения)
test('Пропуск при определенных условиях', async ({ page, browserName }) => {
  // Пропустить в Firefox
  test.skip(browserName === 'firefox', 'Firefox пока не поддерживает эту функцию');

  // Логика теста выполняется только в браузерах, кроме Firefox
  await page.goto('https://www.example.com/');
});

test.fail() Пометить ожидаемый сбой

test.fail()Используется для пометки теста, который, как известно, не проходит.

Пример

// Помечен как ожидаемый сбой (не будет засчитан как сбой теста)
test.fail('Известная ошибка: функция поиска ещё не реализована', async ({ page }) => {
  await page.goto('https://www.example.com/');
  // Ожидается, что эта проверка не пройдёт
  await expect(page.locator('.search-results')).toBeVisible();
});

// Если тест неожиданно пройдёт, это будет отмечено как сбой
// Это напоминает: "Ошибка исправлена, теперь можно удалить маркер test.fail"

test.fail()Его цель — обратная пометка: сбой теста = как ожидалось, прохождение теста = аномалия (маркер, возможно, нужно удалить).


test.only() Запуск только текущего теста

test.only()Используется для запуска только определённых тестов во время разработки и отладки, игнорируя другие тесты в файле.

Пример

// Во время разработки и отладки запускать только текущий тест
test.only('Я отлаживаю этот тест', async ({ page }) => {
  // Будет запущен только этот тест
  await page.goto('https://www.example.com/');
});

test('Этот тест будет проигнорирован', async ({ page }) => {
  // Из-за test.only() выше это не будет запущено
});

test.only()— это инструмент отладки, и его не следует коммитить в репозиторий кода. Если выplaywright.config.tsустановите ... вforbidOnly: true, CI обнаружитonlyи приведёт к сбою сборки.


test.fixme() Отметить как требующий исправления

test.fixme()иtest.skip()Аналогично, но семантически это означает "этот тест нужно исправить."

Пример

// Помечен как требующий исправления (не будет запущен)
test.fixme('Процесс входа нужно обновить из-за изменений API', async ({ page }) => {
  // Этот тест пока не будет запускаться
});

test.slow() Пометить медленные тесты

test.slow()Увеличивает тайм-аут теста в 3 раза.

Пример

// Помечен как медленный тест, тайм-аут утроен
test.slow('Тест с большим объёмом данных', async ({ page }) => {
  // Тайм-аут по умолчанию для этого теста составит 90 секунд (30s × 3)
  // Подходит для сценариев, требующих интенсивной загрузки данных или сложных вычислений
});

Тестовые хуки

Функции-хуки позволяют выполнять настройку и очистку на разных этапах тестирования.

test.beforeEach() и test.afterEach()

beforeEachВыполняется перед каждым тестом,afterEachи выполняется после каждого теста.

Пример

// Путь к файлу: tests/hooks-demo.spec.ts
import { test, expect } from '@playwright/test';

test.beforeEach(async ({ page }) => {
  // Переходить на ту же страницу перед каждым тестом
  await page.goto('https://www.example.com/');
});

test.afterEach(async ({ page }) => {
  // Выполнять очистку после каждого теста (например, удалять созданные данные)
  // Playwright автоматически очищает контексты браузера,
  // Под очисткой здесь в основном подразумевается состояние на стороне сервера
  console.log('Тест завершён, начинаю очистку...');
});

test('Первый тест', async ({ page }) => {
  // страница уже находится на главной странице EXAMPLE
  await expect(page).toHaveTitle(/EXAMPLE/);
});

test('Второй тест', async ({ page }) => {
  // страница также уже находится на главной странице EXAMPLE
  // Это совершенно новый контекст; куки/хранилище из предыдущего теста не сохранятся
  await expect(page.locator('nav')).toBeVisible();
});

test.beforeAll() и test.afterAll()

beforeAllВВ текущей области видимости,выполняется один раз перед всеми тестами,afterAllи выполняется один раз после всех тестов.

Пример

test.describe('Управление пользователями', () => {
  test.beforeAll(async ({ browser }) => {
    // Создать тестового пользователя перед всеми тестами (выполняется только один раз)
    // Примечание: beforeAll не может использовать фикстуру page
  });

  test.afterAll(async ({ browser }) => {
    // Удалить тестового пользователя после всех тестов (выполняется только один раз)
  });

  test('Просмотр списка пользователей', async ({ page }) => { /* ... */ });
  test('Редактирование информации о пользователе', async ({ page }) => { /* ... */ });
});

beforeAllиafterAllне удается получить доступpageк фикстуре, потому что page независим для каждого теста. Если вам нужно работать с page, используйтеbeforeEach。


Область действия хуков и порядок выполнения

Область действия хуков зависит от того, где они определены:

Пример

// Хук уровня файла — применяется ко всему файлу

test.beforeEach(async () => {
  console.log('Файловый beforeEach');
});

test.describe('Группа A', () => {
  // Хук уровня группы — применяется только к группе A
  test.beforeEach(async () => {
    console.log("beforeEach группы A");
  });

  test('Тест A1', async () => { /* ... */ });
  test('Тест A2', async () => { /* ... */ });
});

test.describe('Группа B', () => {
  // У группы B нет собственных хуков; действует только хук уровня файла
  test('Тест B1', async () => { /* ... */ });
});

Порядок выполнения: внешние хуки выполняются первыми, внутренние — после.

Для A1 и A2: сначала вывод"文件级 beforeEach", затем вывод"分组 A 的 beforeEach"。

Для B1: только вывод"文件级 beforeEach"。

Другие расширения