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

Cypress

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

Тест виконується
Усередині браузера
Команди
У чергу, потім запуск
Найкращий у
Розборі падіння

Тест живе всередині сторінки

Більшість автоматизації браузера працює як дистанційне керування: тест — це програма деінде, що надсилає інструкції браузеру й читає відповіді. Cypress це перевертає. Ваш застосунок завантажується в iframe, код тесту завантажується поруч у тій самій вкладці, і обидва працюють на одному event loop JavaScript. Між тестом і сторінкою немає дроту — тест дістає до застосунку так, як міг би скрипт на самій сторінці.

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

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

Повторність — те, що справді запобігає нестабільності

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

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

Що ви написалиЧи повторюється?Що робити натомість
Запит, за яким іде перевіркаТакНічого: саме на цю форму й розрахована модель.
Два запити в ланцюжку, потім перевіркаЛише другийВикористайте один запит, що знаходить елемент напряму, — тоді повторюється весь пошук.
Колбек, що оглядає результатНіПеренесіть умову в перевірку, щоб цикл повторів її бачив.
Дія — клік або введенняНе повторюєтьсяСпершу чекає готовності, потім виконується один раз. Перевіряйте отриманий стан, а не клік.
Значення, зчитане у змінну й перевірене пізнішеНіЗначення заморожене на момент зчитування. Перевіряйте живий елемент.

Що дає життя всередині сторінки

  • Подорож у часі

    Раннер тримає знімок DOM для кожної команди. Наведіть на крок у журналі — і сторінка покаже, якою вона була тоді: без відтворення й без здогадок, який саме рендер ви бачите.

  • Доступ усередину застосунку

    Оскільки він ділить сторінку, тест може викликати функцію застосунку, прочитати його стан чи стежити за методом — зручно, коли інакше до стану вести шість кліків.

  • Контроль мережі

    Запити можна спостерігати, чекати на них, затримувати чи підміняти. Заглушка на межі робить стан помилки чи порожній стан тривіально тестованим замість нездійсненного.

  • Компонентне тестування

    Той самий раннер монтує окремий компонент у справжньому браузері. Саме тут основна цінність для фронтенд-команд: кейси йдуть із повільного наскрізного рівня, не втрачаючи справжнього DOM.

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

Стеля, названа прямо

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

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

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

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

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

  • Тесту потрібні два користувачі водночас або він регулярно перетинає домени, вкладки чи вікна.
  • Обмеженням є час набору, а дешева паралельність на контекстах змінює арифметику.
  • Потрібен той самий набір на Chromium, Firefox і WebKit з одного файлу.
  • Пріоритет — діагностувати падіння в CI без відтворення, а механізмом є траса для відтворення.

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

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

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

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

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

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

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

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

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