- PVSM.RU - https://www.pvsm.ru -

5 причин почему Playwright лучше Cypress

Перевод статьи Alister Scott [1]Five reasons why Playwright is better than Cypress [2]

Очевидно, что я не фанат Cypress, о чем, собственно, 2 года назад написал пост [3]. За это время мое внимание привлек Playwright. Я специально вернулся к Cypress, чтобы сравнить его с Playwright и посмотреть, были ли исправлены претензии по отношению к первому фреймворку. На основании проведенного сравнения, могу смело рассказать о причинах, почему Cypress все еще проигрывает конкуренцию.

Сравниваемые версии:
Cypress: 8.7.0 on Electron 93
Playwright: 1.16.0

Причина 1: Playwright быстрее чем Cypress

Зафиксированный результат — до 4-х раз быстрее! [4]
Берем простой пример из документации Cypress:

describe('My First Test', () => {
    it('clicking "type" shows the right headings', () => {
      cy.visit('https://example.cypress.io')
      cy.contains('type').click()
      // Should be on a new URL which includes '/commands/actions'
      cy.url().should('include', '/commands/actions')
      // Get an input, type into it and verify that the value has been updated
      cy.get('.action-email')
        .type('fake@email.com')
        .should('have.value', 'fake@email.com')
    })
})

Работает 8 секунд на моем M1 Macbook Air

✔  demo-spec.js                             
00:08

Тот же пример на Playwright:

import { test, expect } from '@playwright/test'
 
test.describe.parallel('My First Test', () => {
  test('clicking "type" shows the right headings', async ({ page }) => {
    await page.goto('https://example.cypress.io')
    await page.click('a:has-text("type")')
    // Should be on a new URL which includes '/commands/actions'
    await page.waitForURL(/.+/commands/actions$/)
    // Get an input, type into it and verify that the value has been updated
    await page.fill('.action-email', 'fake@email.com')
    await expect(page.locator('.action-email')).toHaveValue('fake@email.com')
  })
})
Running 1 test using 1 worker
  ✓  scenarios/compare.cypress.spec.ts:4:3 › My First Test › clicking "type" shows the right headings (1s)
  1 passed (2s)

В 4 раза быстрее!😎

Причина 2: У Playwright крутая поддержка для параллельного запуска тестов на одной машине (локально и на CI без подписки или учетной записи)

Чтобы запустить тесты параллельно, нужно отправить их в Cypress Dashboard [5] (или Sorry Cypress [6]). Только так возможна работа в несколько потоков на одной машине, хотя разработчики Cypress и не рекомендуют заниматься подобным. В то же время, на Playwright параллельные тесты запускаются без проблем.

“While parallel tests can also technically run on a single machine, we do not recommend it since this machine would require significant resources to run your tests efficiently.”

https://docs.cypress.io/guides/guides/parallelization#Overview [7]

Либо Cypress так раздут, что для параллельных тестов требуется дорогое оборудование, либо команда Cypress хочет, чтобы пользователи платили за их службу параллельного выполнения. А может быть, и то, и другое?

Причина 3: Playwright поддерживает параллельные тесты в одном файле, а для Cypress их придется разделять

Например, в Playwright я могу запустить эти 8 тестов параллельно:

import { test, expect } from '@playwright/test'
 
test.describe.parallel('My First Test', () => {
  test('clicking "type" shows the right headings 1', async ({ page }) => {
    await page.goto('https://example.cypress.io')
    await page.click('a:has-text("type")')
    await page.waitForURL(/.+/commands/actions$/)
    await page.fill('.action-email', 'fake@email.com')
    await expect(page.locator('.action-email')).toHaveValue('fake@email.com')
  })
  test('clicking "type" shows the right headings 2', async ({ page }) => {
    await page.goto('https://example.cypress.io')
    await page.click('a:has-text("type")')
    await page.waitForURL(/.+/commands/actions$/)
    await page.fill('.action-email', 'fake@email.com')
    await expect(page.locator('.action-email')).toHaveValue('fake@email.com')
  })
  test('clicking "type" shows the right headings 3', async ({ page }) => {
    await page.goto('https://example.cypress.io')
    await page.click('a:has-text("type")')
    await page.waitForURL(/.+/commands/actions$/)
    await page.fill('.action-email', 'fake@email.com')
    await expect(page.locator('.action-email')).toHaveValue('fake@email.com')
  })
  test('clicking "type" shows the right headings 4', async ({ page }) => {
    await page.goto('https://example.cypress.io')
    await page.click('a:has-text("type")')
    await page.waitForURL(/.+/commands/actions$/)
    await page.fill('.action-email', 'fake@email.com')
    await expect(page.locator('.action-email')).toHaveValue('fake@email.com')
  })
  test('clicking "type" shows the right headings 5', async ({ page }) => {
    await page.goto('https://example.cypress.io')
    await page.click('a:has-text("type")')
    await page.waitForURL(/.+/commands/actions$/)
    await page.fill('.action-email', 'fake@email.com')
    await expect(page.locator('.action-email')).toHaveValue('fake@email.com')
  })
  test('clicking "type" shows the right headings 6', async ({ page }) => {
    await page.goto('https://example.cypress.io')
    await page.click('a:has-text("type")')
    await page.waitForURL(/.+/commands/actions$/)
    await page.fill('.action-email', 'fake@email.com')
    await expect(page.locator('.action-email')).toHaveValue('fake@email.com')
  })
  test('clicking "type" shows the right headings 7', async ({ page }) => {
    await page.goto('https://example.cypress.io')
    await page.click('a:has-text("type")')
    await page.waitForURL(/.+/commands/actions$/)
    await page.fill('.action-email', 'fake@email.com')
    await expect(page.locator('.action-email')).toHaveValue('fake@email.com')
  })
  test('clicking "type" shows the right headings 8', async ({ page }) => {
    await page.goto('https://example.cypress.io')
    await page.click('a:has-text("type")')
    await page.waitForURL(/.+/commands/actions$/)
    await page.fill('.action-email', 'fake@email.com')
    await expect(page.locator('.action-email')).toHaveValue('fake@email.com')
  })
})

на моем Macbook Air занимает всего 6 секунд 😎

npx playwright test "./scenarios/compare.cypress.bulk.spec.ts"
Running 8 tests using 4 workers
  ✓  scenarios/compare.cypress.bulk.spec.ts:4:3 › My First Test › clicking "type" shows the right headings 1 (4s)
	......
8 passed (6s)

Чтобы запустить эти тесты параллельно (с использованием сервера) в Cypress, мне нужно было бы структурировать их как отдельные файлы (по одной спецификации на каждый):

5 причин почему Playwright лучше Cypress - 1

И чтобы провести эти тесты, на той же машине потребуется 45 секунд, что в 7,5 раз медленнее.

5 причин почему Playwright лучше Cypress - 2

Причина 4: Playwright полностью поддерживает синтаксис async/await для чистого и читаемого кода

import { test, expect } from '@playwright/test'
 
test.describe.parallel('My First Test', () => {
  test('clicking "type" shows the right headings', async ({ page }) => {
    await page.goto('https://example.cypress.io')
    await page.click('a:has-text("type")')
    await page.waitForURL(/.+/commands/actions$/)
    await page.fill('.action-email', 'fake@email.com')
    await expect(page.locator('.action-email')).toHaveValue('fake@email.com')
  })
})

Нет цепочек прототипов, никакого сложного для понимания кода, никакой магии. Вы можете видеть, откуда берутся test, expect и page.

Тот же тест в Cypress:

describe('My First Test', () => {
    it('clicking "type" shows the right headings', () => {
      cy.visit('https://example.cypress.io')
     
      cy.contains('type').click()
   
      // Should be on a new URL which includes '/commands/actions'
      cy.url().should('include', '/commands/actions')
   
      // Get an input, type into it and verify that the value has been updated
      cy.get('.action-email')
        .type('fake@email.com')
        .should('have.value', 'fake@email.com')
    })
})

Обратите внимание, как функция вызывает цепочку. Неплохая реализация в данном примере, но происходящее быстро выходит из-под контроля. Нет никаких указаний, что это синхронный код — отсутствует await на вызовы функций. “Магия” происходит в случае с cy и describe, и при этом совершенно отсутствует импорт.

Причина 5. Playwright не нужны плагины

В Cypress много ограничений. Большую часть из них можно исправить плагинами, но некоторые проблемы таковыми остаются ввиду отсутствия “лекарства”.

Нужно нажать клавишу tab? Для этого есть плагин [8]. И, судя по ишью Github [9] он вообще не работает из-за отсутствия поддержки этой самой клавиши. Тема открыта с 2016 года по сей день. Вопрос актуален в течение всего времени с момента, когда я последний раз писал о Cypress.

Нужно загрузить файл? Для этого также есть плагин. [10]

Вам нужно выполнить тесты n раз подряд, чтобы проверить стабильность тестов? Для этого есть плагин. [11]

К счастью, Playwright изначально поддерживает всё вышеописанное и множество других функций без каких-либо плагинов. 😎

Вывод:

Как видите, я все еще не фанат Cypress 😀

Тем более теперь есть инструмент, который стал намного лучше и доступен совершенно бесплатно.

Так что я буду придерживаться вывода двухлетней давности:

Издалека Cypress выглядит как идеальный инструмент для автоматизированного тестирования. Я думаю, что его неправильно позиционируют, как инструмент е2е тестирования, хотя на самом деле фреймворк годится только для проверки компонентов. В нем слишком много ограничений, чтобы заниматься созданием настоящих e2e тестов.
https://alisterbscott.com/2019/07/06/my-oughtts-on-cypress-io [12]/

Автор: Алексей Иванов

Источник [13]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/370287

Ссылки в тексте:

[1] Alister Scott: https://github.com/alisterscott/

[2] Five reasons why Playwright is better than Cypress: https://alisterbscott.com/2021/10/27/five-reasons-why-playwright-is-better-than-cypress/

[3] написал пост: https://alisterbscott.com/2019/07/06/my-thoughts-on-cypress-io/

[4] Зафиксированный результат — до 4-х раз быстрее!: https://blog.checklyhq.com/cypress-vs-selenium-vs-playwright-vs-puppeteer-speed-comparison/

[5] Cypress Dashboard: https://dashboard.cypress.io/

[6] Sorry Cypress: https://github.com/sorry-cypress/sorry-cypress

[7] https://docs.cypress.io/guides/guides/parallelization#Overview: https://docs.cypress.io/guides/guides/parallelization#Overview

[8] Для этого есть плагин: https://www.npmjs.com/package/cypress-plugin-tab

[9] ишью Github: https://github.com/cypress-io/cypress/issues/299

[10] Для этого также есть плагин.: https://www.npmjs.com/package/cypress-file-upload

[11] Для этого есть плагин.: https://github.com/bahmutov/cypress-repeat

[12] https://alisterbscott.com/2019/07/06/my-oughtts-on-cypress-io: https://alisterbscott.com/2019/07/06/my-oughtts-on-cypress-io

[13] Источник: https://habr.com/ru/post/593577/?utm_source=habrahabr&utm_medium=rss&utm_campaign=593577