Структура тестов и хуки в Playwright
В этой главе рассказывается, как организованы тесты Playwright, включая группировку тестов, управление выполнением и использование хуков жизненного цикла.
test.describe() Группировка тестов
test.describe()Используется для объединения связанных тестов в логические группы.
Пример
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('Вход с правильной учетной записью и паролем', 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и выполняется после каждого теста.
Пример
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.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"。