Cypress
Cypress — це раннер браузерних тестів, який ухвалив рішення, якого не ухвалив жоден інший браузерний інструмент: він виконує тест усередині браузера, у тій самій вкладці й тому самому event loop, що й застосунок. Саме тому в нього таке налагодження — можна дивитися запуск крок за кроком і оглядати сторінку на будь-якій команді, — і саме тому певні речі тут незручні або неможливі. Ця сторінка про цю єдину угоду, про чергу команд і модель повторів, що з неї випливають, і про те, як вирішити, чи ця угода пасує вашому набору.
- Тест виконується
- Усередині браузера
- Команди
- У чергу, потім запуск
- Найкращий у
- Розборі падіння
Тест живе всередині сторінки
Більшість автоматизації браузера працює як дистанційне керування: тест — це програма деінде, що надсилає інструкції браузеру й читає відповіді. Cypress це перевертає. Ваш застосунок завантажується в iframe, код тесту завантажується поруч у тій самій вкладці, і обидва працюють на одному event loop JavaScript. Між тестом і сторінкою немає дроту — тест дістає до застосунку так, як міг би скрипт на самій сторінці.
Наслідок, з яким стикаються першим: команди Cypress не є тим, чим виглядають. Рядок, що просить елемент, не дістає його й не повертає — він додає команду в чергу, яка виконається після завершення функції навколо. Саме тому значення не можна присвоїти з команди у змінну й використати наступним рядком: у момент читання наступного рядка ще нічого не виконалося. Усе далі має бути виражене або наступною командою в ланцюжку, або всередині колбека, що приймає результат.
Повторність — те, що справді запобігає нестабільності
Cypress не чекає на сторінку фіксований час. Він повторює останній запит у ланцюжку разом із його перевіркою знову й знову, доки перевірка не пройде або не спливе тайм-аут. Тож перевірка, що текст зʼявився після мережевої відповіді, не потребує очікування: запит повторюється проти живого DOM кожні кілька мілісекунд і вдається тієї миті, коли текст там є.
Важливе правило — і саме воно породжує більшість реальної нестабільності — у тому, що саме повторюється. Повторюється лише останній запит перед перевіркою. Якщо ланцюжок знаходить контейнер, потім рядок усередині, потім перевіряє, повторюється лише пошук рядка: контейнер розвʼязаний один раз і тримається. Коли весь список перерендерюється, той утриманий контейнер відʼєднується, і тест падає з помилкою про елемент, якого вже немає в документі.
| Що ви написали | Чи повторюється? | Що робити натомість |
|---|---|---|
| Запит, за яким іде перевірка | Так | Нічого: саме на цю форму й розрахована модель. |
| Два запити в ланцюжку, потім перевірка | Лише другий | Використайте один запит, що знаходить елемент напряму, — тоді повторюється весь пошук. |
| Колбек, що оглядає результат | Ні | Перенесіть умову в перевірку, щоб цикл повторів її бачив. |
| Дія — клік або введення | Не повторюється | Спершу чекає готовності, потім виконується один раз. Перевіряйте отриманий стан, а не клік. |
| Значення, зчитане у змінну й перевірене пізніше | Ні | Значення заморожене на момент зчитування. Перевіряйте живий елемент. |
Що дає життя всередині сторінки
Подорож у часі
Раннер тримає знімок DOM для кожної команди. Наведіть на крок у журналі — і сторінка покаже, якою вона була тоді: без відтворення й без здогадок, який саме рендер ви бачите.
Доступ усередину застосунку
Оскільки він ділить сторінку, тест може викликати функцію застосунку, прочитати його стан чи стежити за методом — зручно, коли інакше до стану вести шість кліків.
Контроль мережі
Запити можна спостерігати, чекати на них, затримувати чи підміняти. Заглушка на межі робить стан помилки чи порожній стан тривіально тестованим замість нездійсненного.
Компонентне тестування
Той самий раннер монтує окремий компонент у справжньому браузері. Саме тут основна цінність для фронтенд-команд: кейси йдуть із повільного наскрізного рівня, не втрачаючи справжнього DOM.
Двоє посередині заслуговують на застереження. Доступ у нутрощі застосунку робить тест швидшим у написанні й привʼязує його до того, як фіча збудована, а не до того, що вона робить, — тож рефакторинг, який нічого не міняє для користувача, ламає набір. Користуйтеся цим для підготовки стану, а не для перевірки поведінки. А заглушка на кожен запит дає швидкий стабільний набір, що перестав тестувати інтеграцію — а саме заради неї й запускали браузер.
Стеля, названа прямо
Життя всередині вкладки — справжнє архітектурне зобовʼязання, і в нього є краї. Жоден із цих пунктів не є скандалом; усі вони — те, що варто знати до вибору, бо це не проблеми конфігурації, які прибере наступна версія.
- Одна вкладка. Немає другого вікна й попапа, куди перемкнутися; звичний обхід — перевірити, що посилання відкрило б потрібне місце, і перейти туди напряму.
- Домени є окремим випадком. Перехід на інший домен в одному тесті потребує явного блока, що виконує код у тому домені: це працює й не є простою навігацією.
- Усе поза браузером потребує сторони Node: прочитати файл, засіяти базу, викликати сервіс із доступами, яких браузер тримати не повинен.
- Нативні діалоги браузера, вибір файлів і взаємодії рівня ОС лежать поза сторінкою, а отже, поза досяжністю.
- Паралельність — між машинами за файлами специфікацій, а не між контекстами в одному браузері, і саме на її оркестрації заробляє платний сервіс.
Cypress пасує краще, коли
- Тести пишуть і підтримують фронтенд-розробники, і щоденний досвід вирішує, чи робитимуть вони це далі.
- Компонентне тестування у справжньому браузері — велика частина плану, а не думка навздогін.
- Застосунок — вебзастосунок одного домену без сценаріїв із кількома вкладками чи вікнами.
- У команди вже є робочий набір, і зміна інструмента коштує більше, ніж повертає.
Беріть Playwright, коли
- Тесту потрібні два користувачі водночас або він регулярно перетинає домени, вкладки чи вікна.
- Обмеженням є час набору, а дешева паралельність на контекстах змінює арифметику.
- Потрібен той самий набір на Chromium, Firefox і WebKit з одного файлу.
- Пріоритет — діагностувати падіння в CI без відтворення, а механізмом є траса для відтворення.
Антипатерни, які варто назвати
- Чекати фіксовану кількість мілісекунд — саме цю звичку модель повторів і має зробити непотрібною.
- Логінитися через інтерфейс на початку кожного тесту, витрачаючи пʼять секунд на кейс, щоб довести вже доведене раз.
- Умовна логіка тесту за тим, що на сторінці: виходить тест, який не може впасти, бо підлаштовується під усе.
- Заглушити всі запити, аж поки набір перестане тестувати будь-яку інтеграцію, і звітувати про це як про наскрізне покриття.
- Кейси, що залежать від стану, лишеного попередніми кейсами в тому ж файлі: проходять по порядку й падають паралельно.
Коли це застосовувати
Застосовуйте, коли
- Фронтенд-команди, що самі підтримуватимуть набір і щодня цінують зручність налагодження.
- Компонентне тестування у справжньому браузері — щоб зняти кейси з повільного наскрізного рівня.
- Вебзастосунки одного домену без сценаріїв із кількома вкладками, вікнами чи нативними діалогами.
- Там, де заглушка мережі на межі робить тестованими стани помилки й порожнечі, які інакше недосяжні.
Уникайте, коли
- Сценарії, що законно перетинають вкладки, вікна чи кілька доменів: обхідних шляхів там більше, ніж тестів.
- Набори, де головним обмеженням є загальний час: паралельність тут — за файлами специфікацій між машинами.
- Перевірки, написані по нутрощах застосунку: вони привʼязують тест до того, як фіча збудована.
- Як привід переносити тести логіки в браузер: те саме покриття коштує там значно більше і в запуску, і в утриманні.
Було корисно?
Поділіться з тим, хто працює над тією ж задачею.