Równoległość, sharding i Playwright gotowy na CI

Testy Playwright mogą działać równolegle na wielu workerach, a nawet na wielu maszynach. Dowiedz się, jak skonfigurować workery, podzielić pakiety testów dla CI, zbierać artefakty trace i wideo oraz.

Ten wpis jest częścią serii Podstawy Playwright. Jeśli pominąłeś poprzedni post, przeczytaj najpierw Część 7 — Testy wizualne i dostępności w Playwright.


Wprowadzenie — szybkość to funkcja

Pakiet testów, który trwa 45 minut, to pakiet testów, który nie jest uruchamiany. Deweloperzy przestają czekać na CI. Mergują bez zielonych sprawdzeń. Niestabilne testy są ignorowane, ponieważ nikt nie ma czasu na dochodzenie.

Szybkość to nie luksus — to warunek wstępny adopcji.

Playwright jest zaprojektowany z myślą o szybkości. Domyślnie uruchamia testy równolegle w wielu kontekstach przeglądarki. Dzięki shardingowi możesz rozdzielić testy na wiele maszyn CI i skrócić całkowity czas wykonania o 10x lub więcej. Dzięki zbieraniu trace możesz debugować awarie bez ich reprodukowania lokalnie.

Ten post pokaże ci, jak skonfigurować równoległe wykonywanie, jak shardować pakiety testów na workerach CI, jak zbierać i przesyłać artefakty testów (traces, wideo, zrzuty ekranu) oraz jak zbudować pipeline Playwright klasy produkcyjnej w GitHub Actions.


Równoległe wykonywanie: workery i konteksty

Domyślnie Playwright uruchamia testy równolegle, używając workerów. Każdy worker to osobny proces Node.js, który uruchamia podzbiór twoich testów. W ramach każdego workera testy działają sekwencyjnie, ale wiele workerów działa jednocześnie.

Domyślna równoległość

Jeśli masz 10 testów i 5 workerów, Playwright:

  1. Przypisze ~2 testy do każdego workera
  2. Uruchomi 5 workerów jednocześnie
  3. Każdy worker uruchomi przypisane mu testy sekwencyjnie

Wynik: Całkowity czas wykonania to mniej więcej czas najwolniejszych 2 testów, a nie suma wszystkich 10.

Konfigurowanie workerów

Kontroluj liczbę workerów w playwright.config.ts:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  workers: 5, // Użyj 5 równoległych workerów
});

Lub ustaw workery na podstawie dostępnych rdzeni CPU:

export default defineConfig({
  workers: process.env.CI ? 2 : undefined,
  // Na CI: 2 workery (maszyny CI są często ograniczone zasobami)
  // Lokalnie: undefined oznacza, że Playwright wykrywa automatycznie na podstawie rdzeni CPU
});

:::tip[Zacznij konserwatywnie na CI] Runnery CI często mają ograniczone CPU i pamięć. Zacznij od 2–4 workerów i zwiększaj na podstawie obserwowanej stabilności. Zbyt wiele workerów może powodować konkurencję o zasoby i zwiększać niestabilność. :::

Tryb w pełni równoległy

Domyślnie testy w tym samym pliku działają sekwencyjnie w ramach workera. Jeśli chcesz, aby testy w tym samym pliku działały równolegle, włącz fullyParallel:

export default defineConfig({
  fullyParallel: true,
});

Lub per plik:

// tests/checkout.spec.ts
import { test } from '@playwright/test';

test.describe.configure({ mode: 'parallel' });

test('checkout kartą kredytową', async ({ page }) => { /* ... */ });
test('checkout przez PayPal', async ({ page }) => { /* ... */ });

:::warning[Współdzielony stan i w pełni równoległy] Jeśli testy w pliku modyfikują współdzielony stan (rekordy bazy danych, ustawienia konta), włączenie fullyParallel może powodować race conditions. Użyj oddzielnych kont testowych lub wyłącz tryb równoległy dla tych testów. :::

Tryb szeregowy dla testów zależnych od kolejności

Jeśli testy muszą działać w określonej kolejności (np. “utwórz użytkownika” → “edytuj użytkownika” → “usuń użytkownika”), użyj .serial():

test.describe.serial('cykl życia użytkownika', () => {
  test('utwórz użytkownika', async ({ page }) => { /* ... */ });
  test('edytuj użytkownika', async ({ page }) => { /* ... */ });
  test('usuń użytkownika', async ({ page }) => { /* ... */ });
});

Te testy będą działać sekwencyjnie w ramach ich bloku describe, ale inne testy nadal mogą działać równolegle.


Sharding: dystrybucja testów na wiele maszyn

Sharding dzieli twój pakiet testów na N części i uruchamia każdą część na osobnej maszynie. Jeśli masz 100 testów i 4 maszyny CI, każda maszyna uruchamia 25 testów. Całkowity czas wykonania spada do ~25% czasu szeregowego.

Jak działa sharding

Flaga --shard w Playwright przyjmuje stosunek: --shard=X/N oznacza “jestem maszyną X z N wszystkich maszyn.”

Przykład z 4 shardami:

npx playwright test --shard=1/4
npx playwright test --shard=2/4
npx playwright test --shard=3/4
npx playwright test --shard=4/4

Każde polecenie uruchamia inne 25% pakietu testów. Uruchom wszystkie cztery równolegle (na różnych maszynach lub w różnych zadaniach CI), a zmniejszysz całkowity czas wykonania czterokrotnie.

Sharding w GitHub Actions

Oto workflow GitHub Actions klasy produkcyjnej z shardingiem:

name: Playwright Tests

on:
  push:
    branches: [main, develop]
  pull_request:

jobs:
  test:
    timeout-minutes: 30
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shardIndex: [1, 2, 3, 4]
        shardTotal: [4]
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install --with-deps chromium

      - name: Run Playwright tests
        run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-results-${{ matrix.shardIndex }}
          path: test-results/
          retention-days: 7

      - name: Upload Playwright report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report-${{ matrix.shardIndex }}
          path: playwright-report/
          retention-days: 7

Ten workflow:

  1. Tworzy 4 równoległe zadania (jedno na shard)
  2. Instaluje zależności i przeglądarki Playwright
  3. Uruchamia 1/4 pakietu testów w każdym zadaniu
  4. Przesyła wyniki testów i raporty HTML jako artefakty

Jeśli masz 200 testów, które trwają 40 minut szeregowo, sharding na 4 maszyny zmniejsza czas CI do ~10 minut.

Adaptacyjny sharding

Jeśli twój pakiet testów jest mały (< 50 testów), sharding może nie pomóc — narzut uruchamiania 4 zadań CI może przekroczyć korzyść z równoległości. Użyj dynamicznego shardingu na podstawie rozmiaru pakietu:

strategy:
  matrix:
    shardIndex: ${{ github.event_name == 'pull_request' && fromJSON('[1, 2]') || fromJSON('[1, 2, 3, 4]') }}
    shardTotal: ${{ github.event_name == 'pull_request' && 2 || 4 }}

To uruchamia 2 shardy na PR (szybszy feedback) i 4 shardy na głównej gałęzi (głębsze pokrycie).


Artefakty: Traces, wideo i zrzuty ekranu

Gdy testy padają w CI, potrzebujesz danych diagnostycznych. Playwright automatycznie przechwytuje traces, wideo i zrzuty ekranu.

Traces

Trace to pełne nagranie wykonania testu: każda akcja, każde żądanie sieciowe, każdy snapshot DOM. Możesz otworzyć to w przeglądarce trace Playwright i przechodzić przez test, jakbyś debugował go na żywo.

Skonfiguruj zbieranie trace w playwright.config.ts:

export default defineConfig({
  use: {
    trace: 'on-first-retry',
    // Opcje: 'off', 'on', 'retain-on-failure', 'on-first-retry'
  },
});

Zalecane ustawienia:

  • Lokalnie: 'off' (traces są duże i wolne)
  • CI: 'on-first-retry' (przechwytuj traces tylko dla niestabilnych lub padających testów)

Wideo

Wideo pokazują dokładnie, co przeglądarka zrobiła podczas testu. Są łatwiejsze do przejrzenia niż traces, ale mniej szczegółowe.

export default defineConfig({
  use: {
    video: 'retain-on-failure',
    // Opcje: 'off', 'on', 'retain-on-failure', 'on-first-retry'
  },
});

Zrzuty ekranu

Playwright automatycznie przechwytuje zrzuty ekranu przy awarii. Możesz też robić zrzuty ekranu ręcznie:

test('strona główna się ładuje', async ({ page }) => {
  await page.goto('/');
  await page.screenshot({ path: 'homepage.png' });
});

Przesyłanie artefaktów w CI

Artefakty są bezużyteczne, jeśli pozostaną na maszynie CI. Prześlij je, aby deweloperzy mogli je pobrać i przejrzeć:

- name: Upload artifacts on failure
  if: failure()
  uses: actions/upload-artifact@v4
  with:
    name: test-artifacts-${{ matrix.shardIndex }}
    path: |
      test-results/
      playwright-report/
    retention-days: 7

Gdy test pada, przejdź do uruchomienia GitHub Actions, pobierz artefakt i otwórz trace:

npx playwright show-trace trace.zip

Przeglądarka trace otwiera się w twojej przeglądarce z pełną osią czasu wykonania testu.


Ponowne próby: obsługa niestabilnych testów

Rzeczywiste aplikacje są niestabilne. Żądania sieciowe wygasają. API przejściowo zwracają błędy 500. Animacje zakłócają kliknięcia. Ponawianie nieudanych testów zmniejsza fałszywe negatywy.

Skonfiguruj ponowne próby w playwright.config.ts:

export default defineConfig({
  retries: process.env.CI ? 2 : 0,
  // Na CI: ponów nieudane testy do 2 razy
  // Lokalnie: nie ponawiaj (szybko pada, więc naprawiasz problem)
});

Gdy test pada, Playwright:

  1. Uruchamia test ponownie (pierwsza próba)
  2. Jeśli nadal pada, uruchamia go ponownie (druga próba)
  3. Jeśli nadal pada, oznacza test jako nieudany

Jeśli jakakolwiek próba się powiedzie, test jest oznaczony jako niestabilny (passed with retries). Niestabilne testy przechodzą build CI, ale są oznaczone w raporcie, abyś mógł zbadać.

:::warning[Ponowne próby maskują problemy] Ponowne próby zmniejszają niestabilność, ale ukrywają przyczyny źródłowe. Śledź liczbę niestabilnych testów w swoich metrykach CI. Jeśli test jest niestabilny w ponad 10% przypadków, napraw test lub aplikację, nie ponawiaj w nieskończoność. :::


Reporter Playwright dla CI

Wbudowany reporter HTML Playwright jest świetny do lokalnego rozwoju, ale zbyt ciężki dla logów CI. Użyj reportera list dla CI:

export default defineConfig({
  reporter: process.env.CI
    ? [['list'], ['html', { open: 'never' }]]
    : [['html', { open: 'on-failure' }]],
});

Ta konfiguracja:

  • Na CI: Wyświetla listę wyników testów linia po linii do stdout, generuje raport HTML, ale go nie otwiera
  • Lokalnie: Otwiera raport HTML automatycznie przy awarii

Możesz też zintegrować z reporterami stron trzecich:

  • GitHub Actions: @playwright/test/reporter wyświetla adnotacje na PR
  • JUnit XML: ['junit', { outputFile: 'test-results/junit.xml' }] do integracji z narzędziami do zarządzania testami
  • Allure: allure-playwright dla bogatego raportowania historycznego

Zmienne środowiskowe i sekrety

Testy często potrzebują kluczy API, danych uwierzytelniających bazy danych lub tokenów OAuth. Nigdy nie commituj sekretów do kontroli kodu źródłowego.

Używanie GitHub Secrets

Przechowuj sekrety w ustawieniach repozytorium GitHub:

  1. Przejdź do Settings → Secrets and variables → Actions
  2. Dodaj sekrety (np. TEST_USER_EMAIL, TEST_USER_PASSWORD, API_KEY)
  3. Odwołuj się do nich w workflow:
- name: Run Playwright tests
  env:
    TEST_USER_EMAIL: ${{ secrets.TEST_USER_EMAIL }}
    TEST_USER_PASSWORD: ${{ secrets.TEST_USER_PASSWORD }}
    API_KEY: ${{ secrets.API_KEY }}
  run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}

W swoich testach czytaj z process.env:

test('użytkownik może się zalogować', async ({ page }) => {
  await page.goto('/login');
  await page.fill('input[name="email"]', process.env.TEST_USER_EMAIL!);
  await page.fill('input[name="password"]', process.env.TEST_USER_PASSWORD!);
  await page.click('button[type="submit"]');
});

:::tip[Szybko pada przy brakujących sekretach] Dodaj krok walidacji na początku swojego pakietu testów:

// global-setup.ts
if (!process.env.TEST_USER_EMAIL || !process.env.TEST_USER_PASSWORD) {
  throw new Error('Brakuje wymaganych zmiennych środowiskowych: TEST_USER_EMAIL, TEST_USER_PASSWORD');
}

To powoduje natychmiastową awarię buildu z jasnym komunikatem, zamiast 50 niezrozumiałych awarii testów. :::


Kompletny workflow GitHub Actions

Oto pipeline Playwright CI klasy produkcyjnej:

name: Playwright Tests

on:
  push:
    branches: [main, develop]
  pull_request:
  workflow_dispatch:

jobs:
  test:
    timeout-minutes: 30
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shardIndex: [1, 2, 3, 4]
        shardTotal: [4]

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install --with-deps chromium

      - name: Run Playwright tests
        env:
          TEST_USER_EMAIL: ${{ secrets.TEST_USER_EMAIL }}
          TEST_USER_PASSWORD: ${{ secrets.TEST_USER_PASSWORD }}
        run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-results-${{ matrix.shardIndex }}
          path: test-results/
          retention-days: 7

      - name: Upload Playwright HTML report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report-${{ matrix.shardIndex }}
          path: playwright-report/
          retention-days: 7

  merge-reports:
    if: always()
    needs: test
    runs-on: ubuntu-latest
    steps:
      - name: Download all reports
        uses: actions/download-artifact@v4
        with:
          path: all-reports

      - name: Merge reports
        run: npx playwright merge-reports ./all-reports

      - name: Upload merged report
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report-merged
          path: playwright-report/
          retention-days: 30

Ten workflow:

  1. Uruchamia testy w 4 równoległych shardach
  2. Przesyła indywidualne raporty shardów
  3. Łączy wszystkie raporty shardów w jeden raport HTML
  4. Przesyła scalony raport

Deweloperzy mogą pobrać scalony raport i zobaczyć wszystkie wyniki testów w jednym miejscu.


Typowe pułapki CI

Pułapka 1: Nie instalowanie zależności przeglądarki

Przeglądarki Playwright potrzebują zależności systemowych (czcionki, biblioteki). Jeśli widzisz błędy takie jak “Could not find browser binary,” zainstaluj je:

npx playwright install --with-deps chromium

Pułapka 2: Uruchamianie wszystkich przeglądarek w CI

Nie uruchamiaj Chromium, Firefox i WebKit w każdym uruchomieniu CI. To potraja czas wykonania przy minimalnej wartości. Uruchamiaj Chromium domyślnie:

export default defineConfig({
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
  ],
});

Uruchamiaj testy międzyprzeglądarkowe według harmonogramu (nocnie) lub ręcznie przed wydaniami.

Pułapka 3: Ignorowanie niestabilnych testów

Jeśli skonfigurujesz retries: 3 i zignorujesz ostrzeżenia o niestabilnych testach, zbudowałeś system CI, który ukrywa problemy. Ustaw budżet niestabilnych testów (np. “nie więcej niż 2% wskaźnik niestabilności”) i badaj, gdy go przekroczysz.


Podsumowanie

Playwright jest zaprojektowany z myślą o szybkości i skali. Równoległe wykonywanie, sharding i zbieranie trace przekształcają wolny, niewiarygodny pakiet testów w szybki, debugowalny pipeline CI.

Kluczowe wnioski:

  • Użyj workerów, aby uruchamiać testy równolegle lokalnie
  • Użyj shardingu, aby rozdzielić testy na wiele maszyn CI
  • Zbieraj traces i wideo tylko przy awarii, aby zminimalizować koszt przechowywania
  • Skonfiguruj ponowne próby na CI, aby obsłużyć przejściową niestabilność
  • Przesyłaj artefakty, aby deweloperzy mogli debugować awarie bez reprodukowania lokalnie
  • Użyj GitHub Secrets dla danych uwierzytelniających, nigdy nie commituj ich do kontroli kodu źródłowego

:::tip[Mierz i iteruj] Śledź swoje metryki CI: całkowity czas wykonania, wskaźnik niestabilnych testów, wskaźnik przejść, rozmiar artefaktów. Ustaw cele (np. “wszystkie testy działają w mniej niż 10 minut”) i optymalizuj swoją konfigurację, aby je osiągnąć. :::

Zadanie na ten tydzień: Dodaj sharding do swojego pipeline’u Playwright CI. Zmierz czas wykonania przed i po. Jeśli twój pakiet jest mały (< 50 testów), pomiń sharding, ale skonfiguruj zbieranie trace przy awarii. Pobierz trace z nieudanego testu i poznaj przeglądarkę trace — zobaczysz, dlaczego to najlepsze narzędzie do debugowania testów E2E.


Następny w tej serii: Część 9 — Debugowanie padających testów Playwright jak pro