Playwright: лучшие практики

Эта глава обобщает наиболее важные лучшие практики написания и поддержки тестов Playwright, помогая вам писать стабильные и поддерживаемые тесты.


Философия тестирования

Тестирование видимого пользователю поведения

Автоматизированные тесты должны проверять то, что конечные пользователи могут видеть и с чем могут взаимодействовать, а не проверять детали реализации кода.

Например, тесты должны проверять, что на странице отображается правильный текст, кнопки кликабельны, а отправка формы приводит к переходу на правильную страницу, а не проверять возвращаемое значение функции JavaScript или внутреннюю структуру DOM.

Пример

// Не рекомендуется: тестирование деталей реализации
expect(await page.evaluate(() => window.__store.getState().user.name))
  .toBe('example');

// Рекомендуется: тестировать то, что видят пользователи
await expect(page.getByText('Welcome, example')).toBeVisible();

Изоляция тестов

Каждый тест должен бытьполностью независимымот других тестов и не зависеть от состояния предыдущего теста.

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

Пример

// Не рекомендуется: тесты зависят друг от друга
let createdId;
test('Create resource', async ({ request }) => {
  const resp = await request.post('/api/items');
  createdId = (await resp.json()).id;  // Общее состояние
});
test('Use created resource', async ({ page }) => {
  await page.goto(`/items/${createdId}`);  // Зависит от результата предыдущего теста
});

// Рекомендуется: каждый тест самодостаточен
test('Create and use resource', async ({ request, page }) => {
  const resp = await request.post('/api/items');
  const id = (await resp.json()).id;
  await page.goto(`/items/${id}`);
  await expect(page.getByText('Resource details')).toBeVisible();
});

Принципы приоритета локаторов

Использование локаторов в рекомендуемом порядке может повысить стабильность тестов.

ПриоритетМетодСценарий применения
1getByRole()Элемент имеет явную ARIA-роль
2getByLabel()Элемент формы связан с меткой
3getByPlaceholder()Поле ввода имеет placeholder
4getByText()Элемент имеет явный текст
5getByAltText()У изображения есть атрибут alt
6getByTitle()У элемента есть атрибут title
7getByTestId()Резервный вариант
8locator()CSS/XPath — крайняя мера

Старайтесь не использовать имена CSS-классов в качестве локаторов.

Имена классов склонны к изменениям из-за рефакторинга стилей, что приводит к массовым сбоям тестов.


Избегайте ненужных ожиданий

Используйте механизм автоожидания Playwright, избегайте ручного кодированияsleepилиwaitForTimeout。

Пример

// Не рекомендуется: жёстко заданные ожидания
await page.waitForTimeout(3000);
await page.getByText('Data loading complete').click();

// Рекомендуется: полагайтесь на автоожидание и проверки
await expect(page.getByText('Loading data...')).toBeHidden({ timeout: 10000 });
await page.getByText('Data loading complete').click();

Не тестируйте сторонние сервисы

Тестируйте только то, что вы можете контролировать; не зависьте от доступности сторонних сервисов.

Пример

// Не рекомендуется: зависеть от внешнего CDN или API
await page.goto('https://www.example.com/');
// Страница может загружать Google Analytics, внешние шрифты и т. д.

// Рекомендуется: перехватывать запросы к сторонним сервисам, имитировать внешние сервисы
await page.route('**/*analytics*', route => route.abort());
await page.route('**/external-api/**', route => {
  route.fulfill({ json: { status: 'ok' } });
});

Используйте мягкие утверждения expect.soft()

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

Пример

// Рекомендуется: мягкие утверждения проверяют все поля сразу
await expect.soft(page.getByLabel('Username')).toBeVisible();
await expect.soft(page.getByLabel('Password')).toBeVisible();
await expect.soft(page.getByLabel('Email')).toBeVisible();
await expect.soft(page.getByRole('button', { name: 'Register' })).toBeEnabled();
// Если несколько полей отсутствуют, сообщить обо всех сразу

Разумно организуйте тестовые файлы

ПрактикаОписание
Разделяйте файлы по функциональным модулямlogin.spec.ts、checkout.spec.tsожидание
Используйте describe для группировкиПомещайте связанные тесты вtest.describe()Средняя
Правильно используйте beforeEachВынесите повторяющиеся шаги навигации и настройки вbeforeEach
Описательные названия тестовХорошие названия объясняют «что было сделано» и «что ожидается»
Вынесите общий кодВспомогательные функции, используемые более чем в 3 файлах, следует вынести в общий модуль
Не переусердствуйте с DRY2-3 строки повторения легче поддерживать, чем чрезмерную абстракцию

Общие советы по устранению неполадок

Нестабильные тесты

Причины и решения для нестабильных тестов:

ПричинаРешение
Жёстко заданная задержка недостаточно длиннаяИспользуйте механизм автоожидания вместо фиксированных ожиданий
Зависимость от сторонних сервисовИспользуйтеroute()Мокируйте внешние запросы
Анимации вызывают изменение положения элементаВ конфигурацииanimations: 'disabled'
Остаточные данные между тестамиУбедитесь, что каждый тест независимо создаёт и очищает данные
Логика, связанная со временемИспользуйтеpage.clockФиксированное время

Locator не может найти элемент

Шаги устранения неполадок:

  • Проверьте, находится ли элемент в DOM (используйтеtoBeAttached()а неtoBeVisible())
  • Проверьте, находится ли элемент в области просмотра
  • Проверьте, вложен ли он в iframe
  • Используйте Pick Locator в UI Mode для проверки локатора
  • Проверьте, есть ли несколько совпадающих элементов (Locator по умолчанию требует строгого режима)
Другие расширения