QAБудь-який рівень

Cypress

Cypress — відкритий раннер тестів для вебзастосунків, що виконує тест усередині браузера, у тій самій вкладці й event loop, що й застосунок. Сторінка починається з дванадцяти маршрутів зі сторінки «Наскрізне тестування» так, як магазин написав би їх на Cypress, — сім нативних, пʼять з обхідним шляхом і те одне, що спільне для всіх пʼяти, — далі вкладка й чотири компроміси, які Cypress називає постійними, черга команд, повторність у тому вигляді, в якому вона працює з Cypress 12, ізоляція й бік Node, паралельність за файлами специфікацій, чотири зміни під робочими наборами і де він не допомагає.

Поточна версія
16.2 · жовтень 2026
Тест виконується
Усередині браузера
Паралельність за
Файлом специфікації, через Cloud

Дванадцять маршрутів магазину в Cypress на одному екрані — нативно чи в обхід

Питання, з яким команда приходить на цю сторінку, — чи пасує Cypress її продукту, і чесно відповісти на нього можна лише маршрут за маршрутом. Нижче — дванадцять маршрутів зі сторінки «Наскрізне тестування», той самий інтернет-магазин, — так, як магазин написав би їх на Cypress. Третя колонка каже, що потрібно Cypress для кожного; четверта дає вердикт. «Нативно» означає, що це робить звичайний . «В обхід» означає, що це можна зробити — налаштуванням, окремим блоком чи експериментальним прапорцем — і з ціною, яку рядок називає. Наступні розділи посилаються на маршрути за номерами.

#МаршрутУ CypressВердикт
1Знайти «синій светр», відкрити SKU 30417, оплатити тестовою карткоюПоля картки — iframe платіжного провайдера на іншому домені. Cypress не автоматизує iframe з іншого домену, якщо не вимкнути вебзахист через chromeWebSecurity: false, — і лише в браузерах родини ChromeВ обхід · лише родина Chrome
2Зареєструватися, підтвердити, увійти, перезавантажити — у Chromium і WebKitWebKit працює лише з experimentalWebKitSupport і з установленою збіркою WebKit від Playwright, playwright-webkitВ обхід · експериментально
3Скинути забутий пароль за посиланням із листаcy.task() читає тестову скриньку в Node; cy.visit() відкриває посиланняНативно
4Застосувати SPRING10, змінити кількість, оплатитиТой самий iframe картки, що в маршруті 1; cy.request() читає списання з пісочниціВ обхід · лише родина Chrome
5Оплатити з 3-D Secure: перехід на перевірку банку й назадIframe картки, а потім сторінка банку на іншому домені: її кроки йдуть усередині блока cy.origin(), який отримує значення лише через argsВ обхід · двічі
6Картку відхилено; оплатити ще разIframe картки; cy.request() потім рахує замовленняВ обхід · лише родина Chrome
7Спитати асистента замовлень «де моє замовлення»Віджет чату — власна сторінка магазину. Відтворення моделі задане на сервері, як і з Playwright: cy.intercept() бачить лише те, що надсилає браузерНативно
8Почати повернення на 30-й день; етикетка приходить листомcy.clock() задає дату браузера; тестовий годинник сервера задають через cy.request()Нативно
9Склад підтверджує повернення; гроші повертаютьсяcy.request() надсилає вебхук складу з Node, де CORS не діє; cy.task() опитує скринькуНативно
10Змінити адресу доставки до відвантаженняcy.request() читає тестовий ендпоінт складуНативно
11Оформити замовлення як гість, потім створити акаунтІзоляція тестів очищає сторінку, cookie й обидва вебсховища перед кожним тестом — хоча не IndexedDBНативно
12Агент підтримки повертає гроші за пошкоджений товар у бек-офісіcy.session() один раз логінить агента й відновлює цю сесію для кожного тесту, що її проситьНативно

Команда може оминути , оплачуючи через API провайдера тестовим токеном замість введення картки. Тоді маршрут іде всюди — і перестає доводити шов, заради якого існує, — рядок 4 сторінки «Наскрізне тестування»: сума на екрані — це сума, яку просять списати провайдера. Така форма більшості рішень щодо Cypress. Інструмент пропонує шлях в обхід кожного краю, і кожен такий шлях трохи віддаляє тест від того, що робить клієнт.

Тест живе у вкладці — і чотири компроміси, які Cypress називає постійними

Більшість автоматизації браузера — дистанційне керування: тест — це програма деінде, що надсилає браузеру інструкції й читає відповіді. Cypress це перевертає. Ваш застосунок завантажується в iframe, тест завантажується поруч у тій самій вкладці, і обидва працюють на одному JavaScript. Між тестом і сторінкою немає дроту, тож тест дістає до застосунку так, як міг би скрипт на самій сторінці. Усе поза браузером — файл, база даних, тестова скринька — іде через другий процес, Node.js, з яким тест говорить через cy.task().

Cypress робить те, чого не робить жоден інший браузерний інструмент: він завантажує ваш застосунок в iframe і запускає тест поруч із ним — у тій самій вкладці й тому самому event loop. Саме тому налагодження тут таке, яке воно є. Раннер дістає до застосунку напряму, а не просить драйвер переказати питання, тож він знімає DOM на кожній команді й дає прокрутити запуск назад, а перевірку повторює, доки сторінка не вляжеться, замість падати з першого погляду. Те саме рішення малює й стелю. Усе поза браузером — прочитати файл, засіяти базу, підмінити мережевий виклик на рівні процесу — потребує другого процесу, з яким тест спілкується, а все поза вкладкою, як-от друге вікно браузера, системний діалог файлів чи ланцюжок міждоменних редиректів, у кращому разі незручне. Жодна з половин не є дефектом. Це один архітектурний вибір з обома його наслідками на видноті.

Від Cypress 16, що вийшов 1 вересня 2026 року, одна частина цієї картини змінилася. У Chrome, Chromium та Edge Cypress тепер перехоплює трафік тестів у власній мережі браузера, а не пропускає його через свій проксі, тож застосунок тестують по /2 і HTTP/3 з його справжнім сертифікатом. Firefox, WebKit і Electron досі йдуть старим шляхом крізь проксі. Тож той самий cy.intercept() працює двома різними мережевими шляхами залежно від браузера, і кілька його поведінок між ними різняться.

Власна документація Cypress перелічує ціну життя у вкладці під заголовком «Trade-offs» і чотири з цих компромісів називає постійними. Їх варто прочитати її словами, бо це не проблеми конфігурації, які прибере наступна версія.

Постійний компромісЩо це означаєУ магазині
«Cypress — не інструмент автоматизації загального призначення»Його «солодка точка» — тестувати власний застосунок, а не індексувати чи обходити веб, тестувати продуктивність чи скриптувати чужі сайтиПасує: кожен маршрут тестує магазин
«Команди Cypress виконуються всередині браузера»Код тесту — JavaScript, що виконується в браузері; до бекенду дістаються через cy.task() і cy.request()Скринька й склад ідуть через Node (3, 9, 10)
«Cypress не може керувати двома браузерами одночасно»Один браузер за раз; документація радить підмінити інший бік, а для кількох вкладок — плагін @cypress/puppeteerМаршрут 12 послідовний, тож пасує; живий чат між клієнтом і агентом — ні
«Кожен тест привʼязаний до одного супердомену»Перехід на інший домен потребує блока cy.origin(), а з Cypress 14 піддомен теж рахується іншим доменомСторінка 3-D Secure банку (5)

Та сама сторінка називає три «тимчасові», які Cypress сподівається розвʼязати: немає cy.hover(), немає підтримки нативних чи мобільних подій, а підтримка iframe «дещо обмежена» — фрейми з того самого домену можна запитувати, з іншого — ні. Останній із цих пунктів і вирішив чотири вердикти магазину.

Команди стають у чергу, а не виконуються — чому `await`, `try/catch` і `if` поводяться дивно

Наслідок, з яким стикаються першим: команди Cypress — не те, чим здаються. Документація каже прямо: команди нічого не роблять у момент виклику, а ставлять себе в чергу, щоб виконатися пізніше. Рядок, що просить елемент, не дістає його й не повертає. Він додає команду в чергу, яка починає виконуватися лише після того, як уся функція тесту завершилася. Тож значення не можна присвоїти з команди у змінну й використати наступним рядком: коли читається наступний рядок, ще нічого не виконалося.

Прочитати тест Cypress згори донизу — це приблизно мілісекунда, і на сторінці за цей час нічого не відбувається. Кожен рядок cy лише додає команду в чергу: відкрити магазин, ввести текст у пошук, клікнути товар і перевірити ціну — усе це поставлено в чергу, а не зроблено. Рядок 4 присвоює константі те, що повертає cy.get, а повертає він обʼєкт-ланцюжок — обʼєкт для додавання нових команд, — а не ціну на сторінці. Умова if у рядку 5 виконується одразу, поки функцію тесту ще читають, і порівнює цей обʼєкт із рядком, тож дає false — і ніколи не дасть нічого іншого. Лише коли функція повертається, стартує черга й по черзі виконує рядки 1, 2, 3 і 6 приблизно за три секунди; рядки 4 і 5 на той час давно позаду. Тому await не дає нічого корисного, try і catch навколо команди нічого не ловлять, а значення повертається через .then() чи псевдонім, а перевірка — через .should().

Черга пояснює й налагодження, заради якого Cypress обирають. Оскільки раннер сам виконує кожну команду всередині сторінки, він тримає знімок сторінки для кожної команди, і Command Log дає навести на крок і побачити сторінку такою, якою вона була тієї миті. Команди йдуть одна за одною, ніколи паралельно, і документація називає це свідомим рішенням, а не технічним обмеженням: мета — тест, що поводиться однаково від запуску до запуску.

Повторність від Cypress 12: ланцюжок запитів повторюється з початку

Cypress не чекає на сторінку фіксований час. Він ділить команди на види й повторює ті, що лише читають. Запити — cy.get(), .find(), .contains(), .eq() тощо — звʼязуються з перевірками після них, і якщо перевірка падає, Cypress знову проганяє весь ланцюжок запитів із початку, типово до чотирьох секунд. Дії на кшталт .click() і .type() — не запити: вони чекають, доки елемент стане придатним для дії, повторюючи запити перед собою, поки чекають, і потім діють один раз.

Поради, написані до грудня 2022 року, і чимало тих, що досі їх копіюють, кажуть протилежне: що повторюється лише останній запит перед перевіркою, тож список, який перерендерюється, лишає тест із відʼєднаним елементом. Так було в Cypress до версії 11. Cypress 12 це змінив, і правило, що прийшло на заміну, має одну межу, яку варто засвоїти. Перевірка посередині ланцюжка, щойно пройде, фіксує субʼєкт; запити після неї повторюються від цього зафіксованого субʼєкта, а не з початку. Якщо сторінка перерендериться між двома перевірками, зафіксованого елемента вже немає — і тест падає.

Від Cypress 12 запити звʼязуються з перевіркою після них і повторюються разом. У верхньому ряду cy.get знаходить елементи кошика, first бере перший, а перевірка дивиться, чи він містить SKU 30417; поки перевірка не проходить, Cypress знову проганяє весь ланцюжок із cy.get, до чотирьох секунд, тож список, що перерендерився, просто знаходять заново. Щойно перевірка пройшла, субʼєкт зафіксовано. Другий ряд продовжує від цього зафіксованого li: знайти кількість і перевірити, що там 2. Якщо ця перевірка не проходить, повтор іде лише з find, проти вже зафіксованого li, — і якщо кошик перерендерився між ними, цей li відʼєднано від сторінки, і тест падає, хай яка правильна сторінка тепер. Задокументовані виправлення — розділити ланцюжок на першій перевірці, щоб друга половина знову починалася з cy.get, або покласти обидві перевірки в один should із колбеком, щоб вони повторювалися разом. До Cypress 12 у ланцюжку повторювався лише останній запит — саме цю поведінку досі описує чимало старих порад.
Що ви написалиЩо повторюєтьсяЩо робити
Запити, потім перевіркаУвесь ланцюжок запитів, із початкуНічого: на цю форму й розрахована модель
Запити, перевірка, ще запити, перевіркаДо першої перевірки; після неї — лише від зафіксованого субʼєктаРозділіть ланцюжок на перевірці або покладіть обидві в один .should(callback)
Дія — .click(), .type()Чекає на придатність до дії, повторюючи запити перед собою; потім діє один разПеревіряйте стан, що став наслідком дії, а не саму дію
Значення, взяте в .then() і перевірене пізнішеНічого: значення застигає в момент зчитуванняПеревіряйте живий елемент
cy.getCookie() і читання сховищаВід Cypress 16 це теж запити, і вони повторюютьсяЛанцюжте перевірку безпосередньо до них

Ізоляція, сесії й бік Node

Від Cypress 12 ізоляція тестів увімкнена за замовчуванням. Перед кожним тестом Cypress відкриває about:blank і очищає , localStorage і sessionStorage на всіх доменах, тож маршрут не успадковує стан іншого. Різницю з Playwright варто знати точно. Playwright дає кожному тесту новий контекст браузера — цілий новий профіль. Cypress прибирає той самий браузер між тестами, і його документація каже, що лишається: IndexedDB та інші механізми сховища браузера не очищаються. Магазин, що тримав би гостьовий кошик в IndexedDB, мусив би сам очищати його перед маршрутом 11.

ПотребаКомандаВиконується вЩо варто знати
Увійти раз і використовуватиcy.session()БраузеріКешує й відновлює cookie та обидва вебсховища під ідентифікатором; validate перевіряє, що відновлена сесія ще працює (маршрут 12)
Викликати APIcy.request()Node, на боці CypressНе обмежений CORS чи політикою того самого джерела, тому може надіслати вебхук складу (маршрут 9)
Усе інше поза браузеромcy.task()Node, у setupNodeEvents вашої конфігураціїЧитання скриньки, засівання бази. cy.exec(), що запускав команди оболонки, у Cypress 16 видалили на його користь
Значення конфігураціїCypress.expose() або cy.env()Браузері або NodeCypress 16 видалив Cypress.env() і зробив вибір явним: значення, відкрите браузеру, може прочитати застосунок під тестом і все, що він завантажує, тож секрет лишається в Node

Доступ усередину застосунку заслуговує окремого застереження. Оскільки тест ділить сторінку, він може викликати функції застосунку, читати його стор чи стежити за методом. Це чудово для підготовки стану, до якого інакше вести шість кліків, і погано для перевірки поведінки: перевірка, написана по нутрощах, привʼязана до того, як збудовано фічу, тож рефакторинг, що нічого не змінює для клієнта, ламає набір. Готуйте зсередини; перевіряйте через те, що бачить людина.

Паралельно за файлами специфікацій, під керуванням Cypress Cloud

Cypress запускає один браузер за раз на машині, тож швидший набір означає більше машин. Його паралелізація — за файлами: машинам роздають цілі файли специфікацій, ніколи окремі тести. Роздає їх Cypress Cloud. Кожна машина запускає cypress run --record --parallel, Cloud оцінює тривалість кожної специфікації за попередніми запусками й дає кожній машині по одній, доки не скінчаться. Без Cloud команда все одно може розділити специфікації між машинами вручну, але такий поділ статичний і не вчиться.

Cypress паралелить за файлами специфікацій: машинам роздають цілі файли, ніколи окремі тести. Ті самі дванадцять маршрутів, 790 секунд у Chromium, намальовано двічі на чотирьох машинах. Якщо згрупувати їх у чотири файли за фічами, checkout.cy міститиме маршрути 1, 4, 5 і 6 — 315 секунд, — і запуск не завершиться раніше за цей файл, скільки б машин не було, а три інші машини стоятимуть без діла від 150-ї чи 175-ї секунди. Якщо дати кожному маршруту власний файл, їх можна роздавати від найдовшого, саме так Cypress Cloud робить зі своїми оцінками тривалості кожної специфікації, і запуск завершується на 205-й секунді. Ручний поділ дванадцяти файлів між машинами, по три поспіль, завершується на 225-й. З дванадцятьма маршрутами різниця — близько двох хвилин; із сотнями специфікацій нерівної довжини вона вирішує, чи вміститься набір у мердж. Балансування за тривалістю потребує прапорця --record і Cypress Cloud; без них поділ роблять вручну, і він не вчиться.

У Cloud також налагоджують падіння в CI. Test Replay, що зʼявився в Cypress 13 у серпні 2023 року, записує DOM, мережеві запити й консоль кожного тесту в CI і дає розробнику пройти запуск у браузері пізніше; документація каже, що він доступний на всіх планах Cloud без доплати, у межах лімітів використання. Запис відео з того самого релізу вимкнено за замовчуванням. Обидва факти вказують в один бік: частини досвіду Cypress, що найбільше важать у CI, — балансування й відтворення — це хостований сервіс, і це вибір, який варто робити з розплющеними очима, а не деталь, яку виявляють згодом.

Чотири зміни під робочими наборами

Кожна з цих змін — датована, і опублікував її сам Cypress. Жодна не є дефектом. Кожна зрушила щось, на що набір встиг покластися, а третя колонка — те, що зробило цей зсув справою пообіддя, а не тижня.

ЗмінаЩо вона виявилаЩо захистило набір
Cypress 12, 6 грудня 2022: ізоляція тестів за замовчуванням; запити повторюються ланцюжкомТести, що покладалися на сторінку чи cookie, лишені попереднім тестом, після оновлення впали, хоча в продукті нічого не змінилося.Тести, незалежні з першого дня. testIsolation: false існує як шлях міграції — милиця, якій варто призначити дату прибирання.
Cypress 13, 2023: заблоковано сторонній модуль, що надсилав запуски в Sorry-Cypress і Currents; 7 листопада блокування поширюється на v12Проєкти, чиї паралельні запуски оркеструвалися цими сервісами, не могли оновитися й зберегти їх. Cypress виклав свої причини в окремому дописі: за його версією, ці сервіси користувалися його брендом і застосунком, витрати на який ніс Cypress.Розуміння, що оркестрація паралельності — сервіс, який має власника, і запасний варіант, що не потребує нічийого дозволу: специфікації, розділені між машинами вручну.
Cypress 14.4, 20 травня 2025: Chrome 137 припиняє підтримку --load-extension у брендовому ChromeПлагін @cypress/puppeteer — задокументований спосіб тестувати кілька вкладок — зламався в режимі open і запусках із вікном у брендовому Chrome, як і launchOptions.extensions. Cypress порадив Electron, Chrome for Testing або Chromium; згодом Cypress 16 оголосив Electron застарілим як браузер для тестів.Образи CI, що закріплюють збірку Chrome for Testing чи Chromium, а не той брендовий Chrome, який випадково є на машині: обхід «однієї вкладки» тримається на прапорці, яким володіє виробник браузера.
Cypress 16, 1 вересня 2026: трафік перехоплюється в рідній мережі Chrome; cy.exec() і Cypress.env() видаленоКілька поведінок cy.intercept() змінилися лише в браузерах родини Chrome — req.httpVersion більше не повідомляється, заголовків стиснення в перехоплених відповідях немає, — і кожен тест, що читав конфігурацію в браузері, мусив обрати між значенням, видимим браузеру, і значенням лише для Node.Посібник із міграції, прочитаний до оновлення, і набір, прогнаний у тих браузерах, на які він розрахований, — бо той самий тест тепер іде двома мережевими шляхами залежно від браузера.

Де він не допомагає і як порівнюється

Життя всередині вкладки — справжнє архітектурне зобовʼязання, і кожне обмеження нижче випливає з нього. Жодне не є скандалом; кожне варто знати до вибору, бо наступна версія його не прибере.

  • Iframe з іншого домену — платіжну форму, вбудований вхід — не автоматизувати без вимкнення вебзахисту, а це працює лише в браузерах родини Chrome.
  • Один браузер за раз, тож двох користувачів, що діють разом, — живий чат, агента, що бачить екран клієнта, — з одного боку підмінюють, а не тестують.
  • WebKit експериментальний і приходить через збірку WebKit від Playwright; це теж не Safari.
  • Паралельність — за файлами специфікацій між машинами, а добре її балансувати — це сервіс Cloud.
  • Нативні діалоги, вибір файлів і все на рівні операційної системи лежать поза сторінкою, а отже, поза досяжністю.
  • cy.prompt(), у бета-версії, пише кроки зі звичайної англійської й позначає крок, що відновився, як «Self-healed». Вилікуваний крок — це змінений тест, і він заслуговує на рев’ю, як будь-яка інша зміна.

Cypress пасує краще, коли

  • Тести пишуть і підтримують фронтенд-розробники, і щоденний досвід налагодження вирішує, чи робитимуть вони це далі.
  • Компонентне тестування у справжньому браузері — велика частина плану, а не думка навздогін.
  • Застосунок живе на одному домені, без платіжного iframe, другої вкладки й другого користувача в маршруті.
  • У команди вже є робочий набір, і зміна інструмента коштує більше, ніж повертає.

Беріть Playwright, коли

  • Шлях грошей проходить через iframe провайдера й має йти більш ніж в одному рушії.
  • Маршрутові потрібні два користувачі водночас або він регулярно перетинає домени, вкладки чи вікна.
  • Обмеженням є час набору, а дешева паралельність на одній машині змінює арифметику.
  • Падіння в CI треба діагностувати з файлу, яким ви володієте, без хостованого сервісу.

Антипатерни, які варто назвати

  • Чекати фіксовану кількість мілісекунд — саме цю звичку модель повторів і має зробити непотрібною.
  • Логінитися через інтерфейс на початку кожного тесту — пʼять секунд на кейс, щоб довести те, що cy.session() довів один раз.
  • Умовна логіка тесту за тим, що є на сторінці: виходить тест, який не може впасти, бо підлаштовується під усе.
  • Підміняти кожен запит, аж поки набір не перестане тестувати будь-яку інтеграцію, і звітувати про це як про наскрізне покриття.
  • Один файл специфікації на фічу, тож найдовша фіча задає нижню межу кожного запуску.
  • chromeWebSecurity: false, увімкнений для всього набору заради одного iframe, — і кожен маршрут тихо перестає йти у Firefox і WebKit.

Коли це застосовувати

Застосовуйте, коли

  • Фронтенд-команди, що самі підтримуватимуть набір і щодня цінують зручність налагодження.
  • Компонентне тестування у справжньому браузері — щоб зняти кейси з повільного наскрізного рівня.
  • Вебзастосунки на одному домені, без платіжних iframe, других вкладок і двох користувачів в одному маршруті.
  • Там, де підміна мережі на межі робить тестованими стани помилки й порожнечі, які інакше недосяжні.

Уникайте, коли

  • Маршрути, що вводять дані в iframe третьої сторони й мають іти у Firefox чи WebKit.
  • Сценарії, що законно перетинають вкладки, вікна чи кілька доменів: обхідних шляхів там більше, ніж тестів.
  • Набори, де головним обмеженням є загальний час, а хостований сервіс балансування — не варіант.
  • Перевірки, написані по нутрощах застосунку: вони привʼязують тест до того, як збудовано фічу.

Джерела й метод

На чому стоїть ця стаття

  • 11 джерел
  • Перевірено 19 жовтня 2026 р.

Дослідження з Claude. Пошук і звірку першоджерел зроблено з Claude (Anthropic). Відбір, текст і перевірку фактів — редакція Anatomy of IT Product.

Було корисно?

Поділіться з тим, хто працює над тією ж задачею.