Тестовый набор в Playwright строится вокруг логики модульности и переиспользуемости. Основное правило — каждая тестовая функция должна быть независимой, чтобы её можно было запускать отдельно или параллельно с другими. Для этого используются:
test.describe — группировка тестов по
функционалу или странице приложения.test.beforeEach /
test.afterEach — установка и очистка состояния
перед и после каждого теста.fixtures — создание повторно
используемых объектов, таких как авторизация, подготовка данных или
браузерные контексты.Пример структуры:
import { test, expect } from '@playwright/test';
test.describe('Страница логина', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/login');
});
test('Успешная авторизация', async ({ page }) => {
await page.fill('#username', 'user1');
await page.fill('#password', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
});
test('Ошибка при неверном пароле', async ({ page }) => {
await page.fill('#username', 'user1');
await page.fill('#password', 'wrongpassword');
await page.click('button[type="submit"]');
await expect(page.locator('.error')).toBeVisible();
});
});
Playwright позволяет разделять конфигурации для разных
окружений через файл playwright.config.js.
Ключевые моменты:
baseURL.chromium, firefox,
webkit.workers.timeout,
expect.timeout.Пример конфигурации:
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
timeout: 30000,
retries: 1,
use: {
headless: true,
baseURL: 'https://example.com',
viewport: { width: 1280, height: 720 },
actionTimeout: 10000,
},
projects: [
{ name: 'chromium', use: { browserName: 'chromium' } },
{ name: 'firefox', use: { browserName: 'firefox' } },
],
});
Разделение проектов позволяет запускать один и тот же тест на разных браузерах без дублирования кода.
Стабильность достигается сочетанием ожиданий элементов, синхронизации с DOM и обработки асинхронных операций. Основные приёмы:
expect:await expect(page.locator('button#submit')).toBeEnabled();
await expect(page).toHaveURL(/dashboard/);
const loginButton = page.getByRole('button', { name: 'Войти' });
await loginButton.click();
Использование getByRole, getByLabel и
других методов повышает читабельность и устойчивость тестов к
изменениям UI.
await page.waitForResponse(resp => resp.url().includes('/api/login') && resp.status() === 200);
Playwright поддерживает масштабирование тестов:
workers > 1) ускоряет
выполнение, но требует, чтобы тесты были изолированы и не зависели от
общего состояния.test.serial) используется для
сценариев, где порядок действий критичен, например при сложных сценариях
e2e.Пример использования последовательного теста:
test.describe.serial('Сценарий корзины', () => {
test('Добавление товара', async ({ page }) => { /* ... */ });
test('Оформление заказа', async ({ page }) => { /* ... */ });
});
Эффективная поддержка тестового набора невозможна без подробного логирования и скриншотов:
page.screenshot({ path: 'screenshots/test.png' }) для
фиксации состояния страницы.page.video().saveAs() для записи видео выполнения.trace.start() и trace.stop() для создания
трассировки, которая позволяет просматривать шаги теста в Playwright
Trace Viewer.Эти инструменты помогают быстро находить причины падений и выявлять нестабильные места тестового набора.
С ростом приложения тестовый набор нуждается в постоянной реорганизации:
class LoginPage {
constructor(page) { this.page = page; }
async login(username, password) {
await this.page.fill('#username', username);
await this.page.fill('#password', password);
await this.page.click('button[type="submit"]');
}
}
Важный аспект — метрики стабильности:
retries для автоматического повторного запуска падающих
тестов.Для повторяемости тестов необходимо чётко контролировать состояние приложения и данные:
const randomEmail = `user_${Date.now()}@example.com`;
await page.fill('#email', randomEmail);
Такой подход делает тесты предсказуемыми и легко поддерживаемыми при масштабировании.