За последний месяц я насчитал минимум семь свежих статей с заголовком в духе «Playwright быстрее Selenium на N%». Проблема в том, что N у всех разный: 23%, 42%, 63%, «1.85x». Методология почти нигде не раскрыта дальше фразы «controlled environment». Для решения, которое определяет CI-бюджет и архитектуру тестов на годы вперёд, это не цифры — это шум.
Здесь — что из этого шума реально на что-то опирается, почему остальное несопоставимо, и рабочий бенчмарк-харнесс, который вы можете прогнать на своём стеке за один CI-job и получить именно свои числа, а не чужие проценты.

Почему вообще должна быть разница
Все четыре инструмента по-разному говорят с браузером, и это архитектурное решение — не деталь, а главная причина разницы в скорости.
Selenium работает через протокол WebDriver: команда из теста идёт HTTP-запросом к драйверу браузера (chromedriver/geckodriver), тот транслирует её в нативный вызов. Каждое действие — это запрос-ответ по HTTP, и эта связка добавляет задержку на каждом шаге. Selenium 4 добавил протокол BiDi (двунаправленный, поверх WebSocket) для части операций — сетевого перехвата, логов консоли, — но базовое исполнение команд по-прежнему идёт через HTTP-слой WebDriver.
Cypress в принципе не выходит за пределы браузера: тестовый раннер исполняется в том же event loop, что и тестируемое приложение, без какого-либо внешнего протокола между ними. Это даёт нулевую сетевую задержку на каждое действие, но ценой архитектурных ограничений — Cypress не может по-настоящему управлять вторым табом, потому что сам раннер занимает iframe рядом с приложением.
Playwright подключается к браузерам напрямую по их нативным debug-протоколам — Chrome DevTools Protocol для Chromium, патченый Marionette для Firefox, WebKit Inspector Protocol для WebKit — через постоянное WebSocket-соединение. HTTP-слоя между тестом и браузером нет в принципе.
WebdriverIO — особый случай: он может работать и через классический WebDriver-протокол (тогда архитектурно близок к Selenium), и через DevTools/BiDi-протокол. Поэтому в части бенчмарков WDIO попадает в категорию «быстрый», а в части — в категорию «как Selenium», и это зависит исключительно от того, какой драйвер выбрал автор теста.
Из этого уже можно сделать архитектурный вывод без единого замера: WebSocket-протоколы (Playwright, WDIO в DevTools-режиме) структурно не могут быть медленнее, чем HTTP-polling (классический WebDriver), при прочих равных. Вопрос не «будет ли разница», а «насколько она проявится на конкретном тест-сьюте».
Что из опубликованного действительно похоже на бенчмарк
Один из немногих источников, который раскрывает методологию — серия постов Checkly (компания, которая делает synthetic monitoring, то есть сама эксплуатирует эти инструменты в проде, а не продаёт TMS или дашборд для них). Они гоняли одинаковые E2E-сценарии против реальных сайтов на одинаковой инфраструктуре несколько тысяч раз подряд.
Несколько выводов оттуда, которые стоит учитывать:
-
В сценарии с короткими одиночными тестами разрыв между Cypress и остальными инструментами был выражен сильнее всего из-за времени старта раннера; на сьютах из нескольких тестов подряд этот разрыв ощутимо сокращается за счёт амортизации стартовых издержек.
-
При исполнении сьюта (а не одиночного теста) Cypress оказался медленнее самого быстрого инструмента (Playwright) примерно на 23%, и при этом лишь на ~3% медленнее, чем связка WebDriverIO+Selenium, которая в этом прогоне была самой медленной.
-
WebDriverIO в режиме DevTools-протокола показал заметно более высокую вариативность времени исполнения, чем остальные инструменты — то есть медиана может быть неплохой, а вот p95 — нет, и для CI это часто важнее средней цифры.
-
В реальных сценариях именно Playwright показал наименьшую вариативность времени исполнения среди всех протестированных инструментов, что для CI-стабильности значит больше, чем чистая скорость.
Отдельно, по доле рынка — это не бенчмарк скорости, но это реальные данные опроса, а не маркетинговый текст: по данным State of JS 2024, доля Playwright выросла с 9% в 2023 году до 15% в 2024-м, и опрос назвал его самой быстрорастущей по принятию технологией тестирования того года.
Это, по сути, всё, что я нашёл с раскрытой методологией и без явного коммерческого интереса в результате.
А вот это — нет
Дальше идёт ворох свежих (2026 год) постов от компаний, которые продают платформы вокруг этих фреймворков — TMS-аналитику, дашборды для CI, AI-ревью прогонов. Формально у них тоже написано «controlled benchmark», но цифры между собой не бьются, и это видно невооружённым глазом:
|
Источник |
Заявленная разница (Playwright vs Selenium) |
Как обоснована |
|---|---|---|
|
TestDino (блог №1, февраль 2026) |
«на ~290 мс/действие vs 536 мс у Selenium» |
Без описания стенда и кол-ва прогонов |
|
TestDino (блог №2, апрель 2026) |
«на 42% быстрее» |
Ссылка на «внутреннюю аналитику» компании |
|
Techoral |
«на 63% быстрее, на 82% меньше флака» |
«1000 тестов», окружение не описано |
|
Vervali Systems |
«в 1.85 раза быстрее» |
Без сырых данных |
Разброс от 42% до 63% при формально одинаковой постановке вопроса — это не погрешность измерения, это разные (или вообще не проводившиеся) замеры, упакованные в уверенный тон. Часть этих текстов прямо признаётся, что данные «собраны из трёх источников», то есть это уже не бенчмарк, а агрегация чужих маркетинговых чисел, выданная за новое исследование. Использовать такие цифры в технической статье на Хабре — то же самое, что цитировать рекламную листовку как research paper.
Вывод: если у вас на проекте есть конкретный тест-сьют и конкретная CI-инфраструктура, единственное число, которому стоит доверять — то, которое вы получите сами. Дальше — харнесс для этого.
Бенчмарк-харнесс: один и тот же сценарий на четырёх инструментах
Идея: один и тот же flow (логин → переход в раздел → клик по трём элементам → проверка состояния), реализованный нативными средствами каждого фреймворка, прогнанный N раз подряд на одной и той же CI-машине, с фиксированными версиями браузеров.
Тестовое приложение
Берите свой реальный staging, либо локальный fixture-сервер на Express — главное, чтобы все четыре инструмента ходили в один и тот же инстанс с одинаковой задержкой сети (то есть localhost, не реальный CDN, иначе сетевой джиттер забьёт разницу между фреймворками).
// fixture-server.js — минимальное SPA-подобное приложение для замераconst express = require('express');const app = express();app.use(express.urlencoded({ extended: true }));app.get('/login', (_, res) => res.send(` <form method="post" action="/login"> <input name="user" /><input name="pass" type="password" /> <button type="submit">Войти</button> </form>`));app.post('/login', (_, res) => res.redirect('/dashboard'));app.get('/dashboard', (_, res) => res.send(` <div id="items"> <button data-id="1">Item 1</button> <button data-id="2">Item 2</button> <button data-id="3">Item 3</button> </div> <div id="status">idle</div> <script> document.querySelectorAll('button').forEach(b => b.onclick = () => document.getElementById('status').innerText = 'clicked-' + b.dataset.id); </script>`));app.listen(3000);
Playwright
// playwright.spec.tsimport { test, expect } from '@playwright/test';test('login + interact flow', async ({ page }) => { await page.goto('http://localhost:3000/login'); await page.fill('input[name=user]', 'qa'); await page.fill('input[name=pass]', 'qa'); await page.click('button[type=submit]'); await page.waitForURL('**/dashboard'); for (const id of [1, 2, 3]) { await page.click(`button[data-id="${id}"]`); } await expect(page.locator('#status')).toHaveText('clicked-3');});
npx playwright test --reporter=json --workers=1 --repeat-each=50 > pw-results.json
Cypress
// cypress/e2e/flow.cy.jsdescribe('login + interact flow', () => { it('runs the flow', () => { cy.visit('http://localhost:3000/login'); cy.get('input[name=user]').type('qa'); cy.get('input[name=pass]').type('qa'); cy.get('button[type=submit]').click(); cy.url().should('include', '/dashboard'); [1, 2, 3].forEach((id) => cy.get(`button[data-id="${id}"]`).click()); cy.get('#status').should('have.text', 'clicked-3'); });});
npx cypress run --reporter json --spec cypress/e2e/flow.cy.js > cy-results.json
WebdriverIO (в DevTools/Bidi-режиме — для честного сравнения с Playwright)
// wdio.flow.spec.jsdescribe('login + interact flow', () => { it('runs the flow', async () => { await browser.url('http://localhost:3000/login'); await $('input[name=user]').setValue('qa'); await $('input[name=pass]').setValue('qa'); await $('button[type=submit]').click(); await browser.waitUntil(async () => (await browser.getUrl()).includes('/dashboard')); for (const id of [1, 2, 3]) { await $(`button[data-id="${id}"]`).click(); } await expect($('#status')).toHaveText('clicked-3'); });});
Selenium (Node binding, классический WebDriver-протокол)
// selenium.flow.jsconst { Builder, By, until } = require('selenium-webdriver');(async () => { const driver = await new Builder().forBrowser('chrome').build(); const t0 = performance.now(); try { await driver.get('http://localhost:3000/login'); await driver.findElement(By.name('user')).sendKeys('qa'); await driver.findElement(By.name('pass')).sendKeys('qa'); await driver.findElement(By.css('button[type=submit]')).click(); await driver.wait(until.urlContains('/dashboard'), 5000); for (const id of [1, 2, 3]) { await driver.findElement(By.css(`button[data-id="${id}"]`)).click(); } await driver.wait(until.elementTextContains(driver.findElement(By.id('status')), 'clicked-3'), 5000); } finally { console.log(JSON.stringify({ duration_ms: performance.now() - t0 })); await driver.quit(); }})();
Для Selenium придётся самостоятельно обернуть замер времени (как выше) — встроенного репортера с таймингами на уровне теста, как у остальных трёх, нет из коробки.
Условия, без которых сравнение бессмысленно
-
Один и тот же раннер:
ubuntu-22.04, фиксированное число vCPU/RAM (GitHub Actions:ubuntu-latest, без переменной нагрузки от соседних джобов в матрице). -
Headless-режим везде — иначе сравниваете не фреймворки, а рендеринг GUI.
-
Фиксированные, явно прописанные версии браузеров (
npx playwright install --with-deps chromium@<version>,chromedriverтой же мажорной версии, что и Chrome в раннере) — рассинхрон версий браузера и драйвера у Selenium даёт случайный шум, который легко спутать с архитектурной разницей. -
Минимум 30–50 повторов на инструмент, отчёт по медиане и p95, а не по одному прогону — отдельные запуски шумят на 20–40% даже в рамках одного фреймворка.
-
Один и тот же flow, написанный максимально close-to-idiomatic для каждого инструмента (не нужно искусственно занижать Selenium через явные
sleep(), которые никто в проде не пишет — но и не нужно подсовывать ему оптимизации, недоступные в реальном коде команды).
Как считать результат
node -e "const fs = require('fs');const runs = JSON.parse(fs.readFileSync(process.argv[1])).map(r => r.duration_ms).sort((a,b)=>a-b);const median = runs[Math.floor(runs.length/2)];const p95 = runs[Math.floor(runs.length*0.95)];console.log({ median, p95, runs: runs.length });" results.json
Прогоните так каждый инструмент трижды в разные дни (CI-раннеры — общий ресурс, шум от соседей реален), сравните медианы и p95 между прогонами одного и того же фреймворка — если они расходятся больше чем на 10–15%, ваш стенд недостаточно стабилен для выводов, и сначала нужно разбираться с этим, а не с выбором фреймворка.
Что можно сказать без замеров, и что нельзя
Без единого личного замера, опираясь только на архитектуру и на источники с раскрытой методологией, можно уверенно сказать:
-
WebSocket-протоколы (Playwright, WDIO в BiDi-режиме) структурно быстрее на каждое отдельное действие, чем HTTP-polling классического WebDriver. Это не зависит от вендора и не может «развернуться» на каком-то конкретном стенде.
-
На сьютах (а не одиночных тестах) разрыв между Cypress и Playwright меньше, чем на коротких прогонах — стартовые издержки амортизируются.
-
Стабильность (variance, p95) для CI часто важнее средней скорости — это явно показано в данных Checkly, и это игнорируют почти все вендорские посты, концентрируясь на одном эффектном проценте в заголовке.
Чего нельзя сказать без своего замера — это любое конкретное «X% быстрее» для вашего стека. Слишком много переменных: размер DOM, количество сетевых запросов на странице, тип selectors (CSS vs XPath vs role-based), параллелизм в CI, версии браузеров. Те же 23% у Checkly и 63% у Techoral — это не противоречие в «правде», это просто два разных приложения и два разных метода счёта.
Резюме
Если вам нужно решение «на сейчас» без замеров — архитектура даёт достаточно сигнала: для нового проекта на вебе Playwright или WDIO в BiDi-режиме — разумный default по скорости и стабильности, Cypress — если важнее DX для фронтенд-команды и сьюты достаточно длинные, чтобы амортизировать старт, Selenium — если у вас уже большая инвестиция в Java/C#/Ruby-инфраструктуру и WebDriver Grid, который работает.
Если решение нужно обосновать цифрами перед руководством — не берите процент из чужого блога. Прогоните харнесс выше на своём стеке, на своём приложении, в своём CI — это займёт один рабочий день и даст вам единственное число, которому вы сможете доверять и которое сможете защитить, если кто-то спросит «а как считали».
ссылка на оригинал статьи https://habr.com/ru/articles/1050168/