Cypress vs Playwright: порівняння у 2026 році

Коротко: вердикт 2026

CypressPlaywright
Новий проєкт⚠️ Якщо команда вже знає✅ Рекомендовано
Safari/WebKit❌ Ні✅ Так
Mobile❌ Ні✅ Емуляція
Паралелізм💰 Платно (Cloud)✅ Безкоштовно
API тести⚠️ Обмежено✅ Повноцінно
Новачок✅ Легша крива⚠️ async/await
Multi-tab, popup❌ Ні✅ Так

Архітектурна різниця (ключове розуміння)

Cypress — в браузері

Тест-код виконується ВСЕРЕДИНІ браузера:

  • ✅ Прямий доступ до DOM без затримок
  • ✅ Time-travel debugging
  • ❌ Неможливі нові вкладки (window.open)
  • ❌ Немає cross-origin iframe
  • ❌ Немає Safari

Playwright — поза браузером

Тест-код виконується в Node.js і керує браузером ззовні через CDP / WebSocket:

Node.js (тест-код)  →  CDP / WebSocket  →  Браузер
  • ✅ Повний контроль над браузером
  • ✅ Safari, Firefox, Chrome, Mobile
  • ✅ Multi-tab, popups, cross-origin
  • ❌ Крутіша крива навчання

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


Синтаксис: той самий тест

Cypress

describe('Auth', () => {
  it('logs in successfully', () => {
    cy.visit('/auth/login')
 
    cy.get('[data-cy=email]')
      .type('user@test.com')
 
    cy.get('[data-cy=password]')
      .type('pass123')
 
    cy.get('[data-cy=submit]')
      .click()
 
    cy.url()
      .should('include', '/dashboard')
 
    cy.contains('Welcome, User')
      .should('be.visible')
  })
})

Playwright

import { test, expect } from '@playwright/test'
 
test('logs in successfully', async ({ page }) => {
  await page.goto('/auth/login')
 
  await page.getByLabel('Email')
    .fill('user@test.com')
 
  await page.getByLabel('Password')
    .fill('pass123')
 
  await page.getByRole('button', { name: 'Login' })
    .click()
 
  await expect(page)
    .toHaveURL(/dashboard/)
 
  await expect(page.getByText('Welcome, User'))
    .toBeVisible()
})

Різниця у підходах:

  • Cypress: синхронний стиль, .then() для роботи зі значеннями
  • Playwright: стандартний async/await, явна асинхронність

Локатори

Cypress — CSS-орієнтований

cy.get('#submit-btn')              // ID
cy.get('.product-card')            // клас
cy.get('[data-cy="email-input"]')  // data-атрибут (рекомендовано)
cy.contains('Submit')              // текст
cy.contains('h2', 'Dashboard')     // тег + текст
 
// В межах контейнера
cy.get('.cart').within(() => {
  cy.contains('Total')
  cy.get('.price').should('exist')
})

Playwright — семантичний

page.getByRole('button', { name: 'Submit' }) // роль
page.getByLabel('Email address')             // label
page.getByPlaceholder('Search...')           // placeholder
page.getByText('Total: $150')               // текст
page.getByTestId('email-input')             // data-testid
 
// Вкладені і фільтровані
page.locator('.cart')
  .getByRole('button', { name: 'Remove' })
page.getByRole('listitem')
  .filter({ hasText: 'In stock' })

Перевага Playwright: getByRole і getByLabel стабільніші при рефакторингу стилів, відображають accessibility.


Підтримка браузерів

БраузерCypressPlaywright
Chrome / Chromium
Firefox✅ (обмежено)✅ (повна підтримка)
Safari / WebKit
EdgeChromium-based✅ нативно
Mobile браузери✅ (емуляція)
Cross-browser одночасно✅ (projects)
// Playwright — запуск в 4 браузерах одночасно
projects: [
  { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
  { name: 'firefox',  use: { ...devices['Desktop Firefox'] } },
  { name: 'webkit',   use: { ...devices['Desktop Safari'] } },
  { name: 'mobile',   use: { ...devices['iPhone 14'] } },
]

API тестування

Cypress

cy.request({
  method: 'POST',
  url: '/api/login',
  body: { email: 'user@test.com', password: 'pass123' }
}).then(resp => {
  expect(resp.status).to.eq(200)
  expect(resp.body).to.have.property('token')
})
 
// Обхід UI через API (популярна практика)
cy.request('POST', '/api/login', creds)
  .its('body.token')
  .then(token => cy.setCookie('auth_token', token))

Обмеження Cypress: немає gRPC, WebSocket нативно; API тести — окремо від UI тестів ментально.

Playwright

// API і UI в одному проєкті
test('combined test', async ({ page, request }) => {
  const { token } = await (await request.post('/api/login', {
    data: { email: 'user@test.com', password: 'pass123' }
  })).json()
 
  await page.goto('/dashboard')
  await page.evaluate(t =>
    localStorage.setItem('token', t), token)
})

Перевага: один інструмент для UI і API, спільні fixtures, єдиний звіт.


Паралелізм

Cypress

# Між файлами — безкоштовно
cypress run --parallel
 
# Всередині файлу — завжди послідовно
# Sharding між CI машинами — потрібен Cypress Cloud ($75+/міс)

Playwright

// playwright.config.ts
export default defineConfig({
  fullyParallel: true,  // кожен тест в окремому worker
  workers: process.env.CI ? 2 : 4,
})
 
// Sharding вбудований — без платних сервісів
// npx playwright test --shard=1/3
// npx playwright test --shard=2/3
// npx playwright test --shard=3/3
CypressPlaywright
Між файламиТак (безкоштовно)Так (за замовч.)
Між тестамиНіТак
ShardingCypress Cloud 💰Вбудований ✅
100 тестів~8–12 хв~2–4 хв

DX і дебаг

Cypress — Time Travel Debugger

npx cypress open

→ GUI в браузері
→ Список тестів
→ Наведи на будь-який крок → бачиш DOM в той момент
→ Автоматичні скріншоти кожного кроку
→ Відео запис за замовчуванням

Плюс: найкращий DX для новачків — інтуїтивно, наочно.

Playwright — Trace Viewer + VS Code

# Trace Viewer — post-mortem аналіз
npx playwright show-trace trace.zip
→ Скріншот кожного кроку
→ Деталі мережевих запитів
→ Console logs

# VS Code Extension
→ Run/Debug тести з редактора
→ Breakpoints в тест-коді
→ Highlight локатора прямо на сторінці

Обмеження Cypress (архітектурні)

// ❌ Нові вкладки — неможливо нативно
// window.open() блокується
 
// ❌ Cross-origin iframe
// cy.get('#payment-iframe')  // не можна перейти всередину
 
// ❌ Кілька вкладок
// Неможливо тестувати "відкрити в новій вкладці"
 
// ✅ Workaround для iframe (тільки same-origin)
cy.get('#iframe').then($iframe => {
  cy.wrap($iframe.contents().find('body'))
    .find('#card-number').type('4242...')
})

Playwright вирішує всі ці обмеження

// ✅ Нові вкладки
const [newPage] = await Promise.all([
  context.waitForEvent('page'),
  page.getByText('Open in new tab').click()
])
 
// ✅ Cross-origin iframe
const frame = page.frameLocator('#payment-iframe')
await frame.getByLabel('Card number').fill('4242...')
 
// ✅ Popup windows
const [popup] = await Promise.all([
  page.waitForEvent('popup'),
  page.getByText('Open popup').click()
])
await popup.waitForLoadState()

Коли що вибирати

Вибирай Playwright якщо:

  • ✅ Стартуєш новий проєкт у 2026
  • ✅ Продукт з iOS/macOS користувачами (потрібен WebKit)
  • ✅ Потрібен один інструмент для UI і API тестів
  • ✅ Важливий паралелізм без платних сервісів
  • ✅ Є multi-tab, popups або cross-origin iframe сценарії
  • ✅ Команда знає JavaScript/TypeScript async/await

Вибирай Cypress якщо:

  • ✅ Команда вже знає Cypress і є велика тест-suite
  • ✅ Новачки без JS досвіду — Time Travel легший для старту
  • ✅ Продукт тільки для Chrome/Firefox
  • ✅ Компонентне тестування React/Vue — Cypress Component Testing дуже зрілий
  • ✅ Важлива наочна демонстрація тестів замовнику

Не мігруй заради міграції

Якщо команда щаслива з Cypress і проєкт не потребує Safari чи мобільних — не мігруй заради міграції. Витрати на переписування тестів рідко виправдані.


Питання на співбесіді

«Чим архітектурно відрізняється Cypress від Playwright?»

Cypress виконує тест-код всередині браузера — це дає чудовий Time Travel debugger, але накладає обмеження: неможливі нові вкладки, cross-origin iframe, Safari. Playwright виконується в Node.js і спілкується з браузером через CDP — звідси повна підтримка всіх браузерів і сценаріїв.

«Який інструмент краще у 2026?»

Для нового проєкту — Playwright: швидший, підтримує Safari і mobile, безкоштовний паралелізм, один інструмент для UI і API. Cypress доцільний якщо команда вже його знає або потрібен Component Testing.

«Чому Playwright швидший?»

Playwright паралельний за замовчуванням — кожен тест у окремому worker. Cypress запускає тести послідовно в межах файлу. При 100 тестах різниця — 2–4 хвилини проти 8–12.