Як ми використовуємо Playwright і що 99% людей в ньому пропускають

💡 Усі статті, обговорення, новини про тестування — в одному місці. Приєднуйтесь до QA спільноти!

Привіт! Продовжую ділитися корисними практичними знахідками для IT-спільноти.

Кілька років тому наша команда для UI-тестів почала використовувати Playwright та TypeScript. За цей час накопичився цікавий досвід, яким хотілося б поділитися — як отримати максимум від Playwright. Особливо в частині звітності й діагностики складних багів.

Ми покращуємо звіти Playwright: додаємо до них корисні логи API-запитів, перевіряємо консоль на наявність помилок і, головне, використовуємо одну функцію, на яку майже ніхто не звертає уваги, але саме вона допомагає у найскладніших кейсах.

Чому Playwright — це найкраще рішення зараз

Playwright вже давно довів свою ефективність:

  • він активно розвивається;
  • має велику спільноту;
  • часті оновлення та багато вбудованих функцій, які дійсно полегшують життя.

Можливо, ви знаєте, що Playwright взаємодіє з браузером інакше. Він не працює через HTTP, а спілкується з браузером напряму через WebSockets. Це дає помітну перевагу у швидкості запуску та стабільності.

Але це ще не все. З точки зору користувача, того, хто пише автотести, Playwright дає справжню гнучкість.

Ось що особисто я для себе виділила:

  • гнучке та інтуїтивно зрозуміле написання селекторів: XPath, CSS, пошук за будь-якими атрибутами;
  • можливість фільтрувати елементи на будь-якому рівні;
  • інструмент Inspect — просто відкриваєш браузер, Playwright записує всі дії, локатори та створює чернетку тесту за кілька кліків.

⚡ Після переходу з Selenium я помітила, що написання первинного драфту тесту почало займати на 20–30% менше часу, навіть на простих сценаріях. Це значно пришвидшує роботу.

У підсумку інструмент стає помічником, а не джерелом проблем.

💬 З власного досвіду скажу: до цього у нашому департаменті всі тести писалися на Java. Ми вирішили перейти на TypeScript + Playwright. Причина була простою: нам потрібно було, щоб фронтенд-розробники могли читати автотести, правити їх і дописувати, особливо якщо в команді немає QA або він один на кілька команд.

Що дивно — всі тестувальники, які раніше писали лише на Java, без проблем перейшли на TypeScript. Хоча майже ніхто до цього з ним не працював. Цей процес пройшов дуже легко.

У сучасних реаліях, коли команди скорочуються, і все частіше один QA працює на кілька команд, такий підхід стає особливо актуальним.

Жодна людина, яка раніше працювала з Selenium, не сказала, що Selenium краще. Максимум — «приблизно однаково». А частіше, що Playwright явно виграє.

Як логувати API-запити прямо у звіті

Часта ситуація: UI перевіряє кнопку, але перед цим потрібно отримати токен, створити продукт, користувача тощо. Такі дії до UI напряму не належать, але необхідні для проходження тесту.

Playwright, звісно, вміє надсилати API-запити. Але якщо просто використовувати request.post, у стандартному звіті ви не побачите ані тіла запиту, ані відповіді.

А це означає: якщо тест падає, то ви не зрозумієте, у чому проблема.

Що ми робимо?

  • Логуємо всі API-запити під час виконання тесту.
  • Наприкінці тесту Playwright автоматично додає ці логи у звіті.

Для цього ми:

1. Створюємо кастомний REST-клієнт, де прописуємо логування запиту.

2. Логуємо вміст запиту.

3. Логуємо відповідь.

class RestClient {
    public async get<T extends object>(
      url: string,
      requestedParams?: any,
      headers?: any
    ): Promise<T | undefined> {
      const context = await request.newContext();
      this.logRequestData('GET', url, requestedParams, headers);
      const serviceResponse = await context.get(url, {
        headers,
        params: requestedParams,
      });
      return this.handleResponse<T>(serviceResponse);
    }
  ...
    private logRequestData(
      method: string,
      url: string,
      requestedParams?: any,
      headers?: any,
      body?: object
    ): void {
      Log.info(`${method} ${url}`);
      if (requestedParams) Log.info(`Request params: ${JSON.stringify(requestedParams)}`);
      if (headers) Log.info(`Headers: ${JSON.stringify(headers)}`);
      if (body) Log.info(`Request body: ${JSON.stringify(body)}`);
    }
  ...
    private async handleResponse<T>(response: APIResponse): Promise<T | undefined> {
      const responseBody = await response.text();
      Log.info(`[API LOG] Status: ${response.status()}`);
      Log.info(`[API LOG] Body: ${responseBody}`);
      if (!responseBody || responseBody.trim() === '') {
        return;
      }
      return JSON.parse(responseBody) as T;
    }
  }
 

4. Запускаємо тести та дивимось звіт.

Що ми отримуємо?

  • Бачимо всі запити й відповіді, включно із заголовками, тілом і статусом.
  • Можемо зрозуміти, на якому кроці зламалося.
  • Достатньо інформації, щоб не шукати у логах вручну.
  • Один вичерпний ресурс прямо у звіті.

Це рішення врятувало нам години досліджень, особливо коли падіння траплялось до UI-кроку.

Як ловити помилки консолі й чому це must-have в UI-тестах

На вигляд усе добре: сторінка відкрилася, все завантажилося, тест пройшов.

Але в консолі можуть бути:

  • failed to load resource;
  • JavaScript-помилки;
  • та інше.

Вони не ламають тест напряму, але можуть зламати бізнес-логіку. Це технічний борг, який одного дня «відгукнеться».

Тому у наші acceptance-тести ми додали ще одну важливу перевірку:

✅ чи немає помилок у консолі браузера під час завантаження сторінки?

Що ми робимо?

  • у базовій фікстурі підключаємо слухач page.on(’console’);
  • фільтруємо повідомлення за рівнем error;
  • та атичимо іх до звіту.

Усе це відбувається автоматично для всіх тестів, які використовують фікстуру з scope: ’test’ та auto: true.

import { test as base, expect } from '@playwright/test';


type Pages = {
    autoConsoleErrorReader: void;
};


export const test = base.extend<Pages>({
    autoConsoleErrorReader: [
        async ({ page }, use, testInfo) => {
            const consoleErrors: string[] = [];


            page.on('console', (consoleMessage) => {
                if (consoleMessage.type() === 'error') {
                    const errorText = consoleMessage.text();
                    console.error(`Found error in console [${errorText}]`);
                    consoleErrors.push(errorText);
                }
            });
            await use();
            // Attach errors to the report if any
            if (consoleErrors.length > 0) {
                await testInfo.attach('Console Errors', {
                    body: consoleErrors.join('\n'),
                    contentType: 'text/plain',
                });
            }


            // Teardown. Automatically executed after any test
            expect(consoleErrors.length, `Found errors in console ${consoleErrors.join()}`).toBe(0);
        },
        { scope: 'test', auto: true },
    ],
});

У репорті це виглядає так:

Що це дає:

  • виявляє помилки, які непомітні при звичайному запуску;
  • допомагає розробникам знаходити баги у статиці, збірці, конфігурації;
  • підвищує якість на рівні, який багато хто може пропустити.

Спойлер: якщо вам недостатньо інформації про те, як саме виникла ця помилка, перегляньте цю статтю до кінця. Там буде відповідь, який інструмент ви можете використати, щоб отримати відповіді на всі ваші запитання.

Степи та читабельність звітів

Коли тестів стає багато, і вони покривають довгі сценарії, звіти швидко перетворюються на «простирадло» з дій.

Щоб покращити читабельність, ми почали групувати дії у степи. Але ми почали робити це більше ніж через рік після старту тестів, бо з часом у нас з’явилося багато складних і довгих сценаріїв, і ми зрозуміли, що читати такі тести незручно.

Тому, якщо ваш проєкт довгостроковий, можна одразу впроваджувати підхід зі степами, щоб потім не довелося рефакторити код.

У коді це реалізується наступним чином:

test('E2E: Full shopping flow with many actions', async ({ page }) => {
  await test.step('Go to login page', async () => {
    await page.goto('https://www.saucedemo.com/');
  });


  await test.step('Login as standard_user', async () => {
    await page.fill('[data-test="username"]', 'standard_user');
    await page.fill('[data-test="password"]', 'secret_sauce');
    await page.click('[data-test="login-button"]');
  });

💬 Будь ласка, не пишіть у коментарях, що потрібно використовувати PageObjectPattern або щось подібне. Код додано як приклад для зручності розуміння. Звісно, у реальному репозиторії ваш код не має виглядати саме так)) Головне, щоб була зрозуміла суть.

Тепер у звіті видно не просто «клік → ввід → перехід», а чіткі логічні блоки:

📦 «Логін користувачем»

📦 «Сортування продуктів»

📦 «Додавання у кошик»

Звіт до:

Звіт після:

Також це зручно для розробників і аналітиків, які читають звіти, але не завжди знають структуру тестів.

Trace Viewer — ваш детектив із багів

Ви напевно чули про Trace Viewer. Але, за моїм досвідом, 80% AQA ним не користуються. А розробники? Якщо відкрити звіт, де є trace, 99% просто проскролять повз нього. Він не є інтуїтивно зрозумілим.

А дарма.

Адже Trace Viewer — це той самий інструмент, який жодного разу нас не підводив, навіть коли баг:

  • відтворювався через раз;
  • виникав «тільки у клієнта»;
  • виникла помілка в консолі, але не було зрозуміле ії джерело.

Коли у нас складна ситуація, перше, що я прошу: «Завантаж трейс і відкрий у браузері».

Що таке трейс і як він працює?

— У конфігурації Playwright вмикаєте trace: ’on’.

— Після запуску тесту у звіті з’являється trace-файл.

— Завантажуєте його і відкриваєте на trace.playwright.dev (trace.playwright.dev/.

Всередині ви побачите:

  • кожен крок браузера;
  • DOM-структуру;
  • мережеві (network) запити;
  • консольні логи;
  • скріншоти;
  • таймінги.

🚀 Я не знаю іншого інструменту, який настільки детально покаже:

  • де саме все пішло не так;
  • що відбувалося до, під час і після помилки;
  • і в якому порядку все зламалось.

Trace Viewer особливо корисний, якщо баг нестабільний, відтворюється не завжди або виникає лише на CI.

Фрагмент конфігурації для увімкнення trace:

//playwright.config.ts


const config: PlaywrightTestConfig = {
    use: {
        trace: 'retain-on-failure',
    },
};

Як виглядає трейс у звіті:

Трейс у звіті

Як виглядає завантажений трейс:

🔥 Якщо ви ще не відкривали трейси — почніть сьогодні.

Якщо ви вже використовуєте Playwright, спробуйте додати хоча б одну з цих ідей у свій проєкт. Якщо тільки плануєте впровадження, сподіваюся, ця стаття допоможе вам отримати від інструмента максимум!

👍ПодобаєтьсяСподобалось26
До обраногоВ обраному19
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Треба більше похвали плейрайту)
І подібно такі статті щороку повторювати бо все одно зʼявляються новачки і їм важливо вчасно дізнатись про щось хороше

Дякую за поширення досвіду, особливо відловлення логів консолі — дуже корисна практика.

Цікаво: в якому контексті використовуєте тести Playwright? E2E на середовищі? E2E в ізоляціі — як тоді ізолюєте залежності? Компонентні тести? Юніт?

Стандартний звіт Playwright, який доступний «з коробки».

Оскільки статтю писав senior, то очікував більш цікаву інформацію: як це все вбудовано в CI/CD, чи комфортно ранити тести паралельно, з якими проблемами команда зіштовхнулась на стадії імплементації. А вийшла стаття про те, що можна знайти у доках плейрайта. Але мої очікування, то мої очікування :)

Дякую, що поділилися своїми очікуваннями! Мені дуже цікаво чути такі думки.

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

Також цікаво було б дізнатися від вас, як від людини, яка працює з Playwright: чи все, про що йдеться у статті, ви використовуєте на практиці? Чи, можливо, щось було новим для вас?

Не автор, але з власного досвіду

як це все вбудовано в CI/CD

— по докам все ок, як в гітхабі так і в гітлабі.

чи комфортно ранити тести паралельно,

так, локально. В CI рекомендовано замість паралельних тестів джоби розбивати на шарди — теж працює.

Ну СДЕТ теж може написати статтю про свій технічний досвід 😉

Хороша та вичерпна стаття про використання фреймворку (перевірена на досвіді, а не просто інформація про Playwright).
Рада, що мої колеги з Playtika пишуть настільки цікаві та корисні статті.

Підписатись на коментарі