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

CI/CD

GitHub Actions для тест-фреймворку

Каркас workflow для Playwright у GitHub Actions

Workflow — YAML-файл у .github/workflows/. Він складається з jobs, які за замовчуванням виконуються паралельно, кожна на своєму раннері. Job — послідовність steps на одній машині (спільна файлова система). Step — або run: (команда shell), або uses: (готова action).

Тригери (on:): pull_request (PR), push (з фільтром гілок), schedule (cron, завжди в UTC), workflow_dispatch (ручний запуск).

name: e2e
on:
  pull_request:
  push:
    branches: [main]
  schedule:
    - cron: '0 2 * * *'
  workflow_dispatch:

jobs:
  e2e:
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npx playwright install --with-deps chromium
      - run: npx playwright test
      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 14

Що тут важливо: timeout-minutes захищає від зависання (за замовчуванням 360 хвилин); npm ci ставить залежності строго за lock-файлом; if: ${{ !cancelled() }} завантажує звіт і після падіння тестів, бо за замовчуванням крок має умову success() і після червоного кроку пропускається.

Кеш, matrix, concurrency і шардування

Кеш залежностей. actions/setup-node@v4 з cache: npm кешує глобальний npm-кеш (~/.npm) за ключем від package-lock.json; npm ci усе одно виконується, але бере пакети з диска. Кешувати саму node_modules зазвичай не радять. Для власних кешів — actions/cache@v4 з ключем, що змінюється разом із lock-файлом: key: pw-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}. Браузери Playwright лежать у ~/.cache/ms-playwright; після cache hit системні залежності все одно треба поставити (npx playwright install-deps), бо вони не кешуються.

Matrix. Одна job, розмножена за параметрами: браузери, версії Node, номери шардів. Кожна комбінація — окрема job на окремому раннері. За замовчуванням fail-fast: true: коли одна комбінація падає, решту, що ще працюють, скасовують. Для повної картини ставлять fail-fast: false.

jobs:
  e2e:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shard: [1, 2, 3, 4]
    steps:
      # ... checkout, setup-node, npm ci, install browsers
      - run: npx playwright test --shard=${{ matrix.shard }}/4 --reporter=blob
      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: blob-report-${{ matrix.shard }}
          path: blob-report
          retention-days: 1

В upload-artifact@v4 імена артефактів у межах запуску мають бути унікальними — тому номер шарда в імені.

Concurrency. Щоб новий пуш у PR скасовував застарілий запуск:

concurrency:
  group: e2e-${{ github.ref }}
  cancel-in-progress: true

Для main скасування зазвичай не вмикають (cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}), щоб не втратити результат запуску на змерджених комітах.

Секрети, service containers, GitLab CI і Jenkins

Секрети. Зберігаються в Settings → Secrets and variables, у workflow читаються як ${{ secrets.API_TOKEN }}, у логах маскуються. Передавайте їх у env: лише тим кроком, якому вони потрібні. Для PR із форків секрети не передаються (це захист від крадіжки); небезпечний pull_request_target з checkout чужого коду використовувати не варто. Нечутливі налаштування (URL оточення, прапорці) — це vars, не secrets.

Service containers. Для інтеграційних тестів, яким потрібна БД, в job додають services: — контейнер піднімається до кроків і зупиняється після job.

jobs:
  api-tests:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: test
          POSTGRES_DB: app_test
        ports: ['5432:5432']
        options: >-
          --health-cmd "pg_isready -U postgres"
          --health-interval 5s
          --health-timeout 5s
          --health-retries 10
    env:
      DATABASE_URL: postgres://postgres:test@localhost:5432/app_test
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npm test

Параметри --health-* змушують job чекати, поки БД справді готова. Якщо job сама виконується в контейнері (container:), до сервісу звертаються за його ім'ям (postgres), а на раннері — через localhost і мапінг порту.

Інші системи — ті самі ідеї. GitLab CI: .gitlab-ci.yml, stages, parallel: 4 замість matrix, cache:, artifacts: when: always, services:. Jenkins: Jenkinsfile з stages і parallel, post { always { ... } } для звітів, агенти замість раннерів. Різниться синтаксис, а не підхід: тригери, кеш, паралелізм, артефакти, секрети.