QA Interview Hub · База знань · Тести

Playwright

Дебаг, trace viewer, звіти, flaky

Діагностика: trace viewer, репорти, flaky-тести

Trace viewer — головний інструмент дебагу: у трейсі записані кожна дія, DOM-знімки до й після, мережа, консоль і вихідний код тесту. У CI зазвичай ставлять trace: 'on-first-retry', щоб не витрачати ресурси на успішні прогони. Відкривають трейс через npx playwright show-trace або з HTML-звіту.

Локально для дебагу є UI mode (--ui) з time-travel, режим --debug з Playwright Inspector і page.pause(). Codegen (npx playwright codegen) записує дії та підказує локатори. Звіти: вбудований HTML, list, JUnit для CI, а також Allure.

Для пошуку flaky використовують --repeat-each=10 і --retries, а в HTML-звіті такі тести позначаються як flaky. Типові причини: спільні дані між воркерами, відсутність web-first assertions, waitForTimeout, анімації, повільне CI-середовище. Лікують першопричину — ретраї лише маскують проблему.

Локальний дебаг: інструменти і коли який

  • npx playwright test --ui — UI mode. Список тестів, watch-режим (перезапуск при зміні файлу), time-travel по кроках з DOM-знімками, мережею і консоллю. Мій основний інструмент при написанні тестів.
  • --debug або змінна PWDEBUG=1 — Playwright Inspector: покрокове виконання, браузер у headed-режимі, таймаути вимкнені. Зручно, щоб зупинитися перед конкретною дією.
  • await page.pause() — точка зупинки в коді, відкриває Inspector саме тут.
  • --headed і launchOptions: { slowMo: 300 } — побачити, що відбувається, у сповільненому темпі.
  • Розширення для VS Code — запуск окремого тесту, breakpoints, кнопка Pick locator, яка показує рекомендований локатор для елемента.

Codegen корисніший, ніж здається, не лише для запису дій:

npx playwright codegen --device="iPhone 13" --lang=uk-UA https://app.test
npx playwright codegen --save-storage=auth.json https://app.test/login
npx playwright codegen --load-storage=auth.json https://app.test/dashboard

Перша команда відкриває застосунок з емуляцією пристрою, друга зберігає стан після ручного логіну, третя відкриває вже залогіненим. Згенерований код я не комічу як є: беру з нього локатори, а структуру переписую під Page Object і фікстури.

Для дебагу в CI цей набір не працює, там основне — trace.

На співбесіді: Під час написання тестів я працюю в UI mode з watch-режимом, для зупинки на конкретному кроці використовую page.pause() або --debug. Codegen беру для швидкого підбору локаторів і збереження стану логіну, але код після нього переписую.

Артефакти і звіти

Що записувати, налаштовується в use:

ОпціяКорисні значення
traceon-first-retry, retain-on-failure, on
screenshotonly-on-failure, on
videoretain-on-failure, on-first-retry

on-first-retry пише артефакт лише при першому ретраї, тому без retries він нічого не дасть. retain-on-failure пише завжди, але зберігає лише для тестів, що впали — це дорожче за ресурсами, зате артефакт є навіть без ретраїв.

Можна одночасно кілька репортерів:

reporter: process.env.CI
  ? [['blob'], ['junit', { outputFile: 'results/junit.xml' }], ['github']]
  : [['html', { open: 'on-failure' }], ['list']],

github додає анотації помилок прямо в PR, JUnit читає Jenkins чи GitLab, HTML — для людей. Для Allure підключають allure-playwright.

Власні дані до звіту додають через testInfo.attach:

test('звірка виписки', async ({ request }, testInfo) => {
  const res = await request.get('/api/statement');
  await testInfo.attach('statement', {
    body: await res.text(),
    contentType: 'application/json',
  });
});

Так у звіті з'являються відповіді API, логи бекенду, згенеровані файли. Коли тест падає, розробник бачить не лише скріншот, а й дані, з якими працював тест.

На співбесіді: У CI я ставлю trace: 'on-first-retry' разом з ретраями, скріншоти лише при падінні, і кілька репортерів: blob для шардів, JUnit для CI і github для анотацій у PR. Додаткові дані, як відповіді API, прикріплюю через testInfo.attach.

Як я системно розбираю flaky-тест

Флакі-тест — це не «просто перезапусти». Мій процес:

  1. Відтворити. npx playwright test checkout.spec.ts --repeat-each=20. Якщо падає локально — добре, є що дебажити.
  2. Перевірити ізоляцію. Запустити з --workers=1 і окремо з --workers=4. Якщо падає лише паралельно — проблема в спільних даних або акаунті між воркерами.
  3. Перевірити порядок. Запустити тест окремо через -g "назва". Якщо окремо проходить, а в наборі падає — від нього залежить інший тест або залишки даних.
  4. Порівняти trace успішного і неуспішного прогону. Часто видно, що на падінні API відповів повільніше, або спінер ще перекривав кнопку.
  5. Класифікувати причину: тест (немає очікування, крихкий локатор), дані, середовище, чи справжній баг застосунку, наприклад race condition у фронтенді.

Останній пункт важливий: частина флакі — це реальні баги, які користувач теж зустріне. Якщо подвійний клік створює два платежі, це не проблема тесту.

Поки причина не виправлена, тест не має блокувати команду. Я позначаю його test.fixme з посиланням на тікет або виношу в карантинний проєкт з тегом @quarantine, який не блокує мерж. Але в карантину має бути власник і строк, інакше він перетворюється на смітник.

Метрика, яку варто відстежувати, — частка флакі-тестів у прогонах. HTML-звіт позначає тест як flaky, якщо він пройшов лише після ретраю.

На співбесіді: Я відтворюю флакі через --repeat-each, перевіряю ізоляцію зміною кількості воркерів і порівнюю trace успішного й неуспішного прогону. Потім класифікую причину, бо частина флакі — це реальні race condition в застосунку. До виправлення тест іде в карантин з тікетом і власником.