Автоматизация тестирования с Cypress становится всё популярнее, а кастомные команды — одним из ключевых инструментов для повышения читаемости, переиспользуемости и поддерживаемости тестов. В этой статье разберём, что такое кастомные команды, почему Cypress рекомендует отказаться от классического Page Object Model (POM), а также рассмотрим, как грамотно организовать и структурировать свои команды.
Что такое кастомные команды в Cypress?
Кастомные команды — это пользовательские функции, которые расширяют базовый набор команд Cypress (cy.*). Они позволяют инкапсулировать часто повторяющиеся действия и операции с UI в удобные вызовы.
Пример базовой кастомной команды:
Cypress.Commands.add('login', (username: string, password: string) => { cy.visit('/login'); cy.get('#username').type(username); cy.get('#password').type(password); cy.get('button[type=submit]').click(); });
Теперь в тестах можно просто вызывать:
cy.login('user', 'password123');
Это упрощает тесты и делает код более декларативным.
Почему Cypress рекомендует отказаться от классического POM?
В официальном блоге Cypress есть статья Stop Using Page Objects and Start Using App Actions, где подробно объясняется, почему классический Page Object Model (POM), унаследованный от Selenium, не подходит для Cypress:
-
POM нарушает цепочку Cypress-команд. Методы классов обычно не возвращают команды
cy.*, из-за чего теряется встроенный механизм ожиданий, логирования, автоматического повторения (retry) и time-travel. -
Усложняет отладку и снижает надёжность тестов. Так как действия вне
cyне попадают в логи и не контролируются Cypress. -
Создаёт лишние абстракции и перегружает тесты. Cypress ориентирован на простоту и прямое взаимодействие с приложением.
Вместо этого Cypress предлагает использовать кастомные команды и App Actions, которые полностью интегрируются в цепочку Cypress и используют преимущества его асинхронной модели.
Как организовать кастомные команды?
1. Где хранить кастомные команды?
По умолчанию Cypress предлагает размещать кастомные команды в одном файле:cypress/support/commands.ts
Этот файл автоматически подгружается перед запуском тестов, и все команды из него доступны глобально.
2. Что делать при большом количестве команд?
В небольших проектах одного файла может быть достаточно, но при росте проекта такой подход приводит к «хаосу» и потере структуры:
-
Трудно понять, какая команда за что отвечает.
-
Появляются дублирующие и плохо документированные команды.
-
Возможны конфликты имён, так как все команды глобальные.
Рекомендуемый подход — разделять команды по смысловым модулям:
cypress/support/commands/ ├─ auth.commands.ts ├─ cart.commands.ts ├─ profile.commands.ts └─ index.ts
В index.ts импортируются все модули команд:
import './auth.commands'; import './cart.commands'; import './profile.commands';
И index.ts подключается в cypress/support/commands.ts или напрямую из support/index.ts.
3. Как именовать команды?
Чтобы избежать конфликтов и улучшить читаемость, полезно использовать префиксы:
-
cy.authLogin() -
cy.cartAddItem() -
cy.profileUpdateName()
Минусы кастомных команд
Хотя кастомные команды — мощный инструмент, нужно помнить о некоторых рисках:
-
Глобальность. Все команды регистрируются глобально, что может привести к перезаписи существующих или встроенных команд. Особенно если все плохо с документацией, над проектом работает много людей или «текучка» в коллективе.
-
Потеря контроля. При большом количестве команд становится сложно отследить, где и как они используются.
-
Трудности с отладкой. Если команда содержит сложную логику, это усложняет диагностику проблем.
Поэтому важно поддерживать документацию, придерживаться модульной структуры и не перегружать команды лишней логикой.
Типизация кастомных команд в TypeScript
Чтобы получить автодополнение и проверку типов в кастомных командах, можно расширить глобальный интерфейс Cypress. Это делается в отдельном файле, например, cypress/support/index.d.ts:
// cypress/support/index.d.ts declare namespace Cypress { interface Chainable<Subject = any> { /** * Логин под пользователем * @example cy.authLogin('user1', 'pass123') */ authLogin(username: string, password: string): Chainable<void>; /** * Добавить товар в корзину по ID * @example cy.cartAddItem('product123') */ cartAddItem(productId: string): Chainable<void>; } }
Теперь при использовании команд IDE будет подсказывать их сигнатуры, а TypeScript — контролировать правильность аргументов.
Best practice по организации тестов и команд
-
Разделяйте команды и тесты по функциональным областям. Это улучшает навигацию и поддержку.
-
Избегайте длинных цепочек команд с побочными эффектами внутри одной команды. Лучше разбивать действия на логические шаги.
-
Документируйте команды. Используйте JSDoc, чтобы описывать назначение и параметры.
-
Пишите тесты, используя только кастомные команды. Это упрощает поддержку тестов и повышает их читаемость.
-
Периодически рефакторьте команды. Убирайте дублирование, упрощайте логику.
Полезные утилиты для кастомных команд
|
Название |
Назначение |
Импорт |
Пример использования |
|---|---|---|---|
|
Генерация фейковых данных для форм и API |
|
||
|
qs |
Формирование query-параметров |
|
|
|
uuid |
Генерация уникальных ID |
|
|
|
dayjs |
Работа с датами |
|
|
|
@testing-library/cypress |
Семантические селекторы для улучшенной доступности |
|
|
Используйте их внутри своих кастомных команд для повышения выразительности тестов.
Заключение
Кастомные команды в Cypress — эффективный способ сделать тесты лаконичнее и понятнее, сохранив преимущества асинхронной модели и мощных возможностей Cypress. Официальная рекомендация — отказаться от классического POM в пользу App Actions и кастомных команд.
Для удобства и поддерживаемости команд стоит использовать модульную структуру, разделяя их по доменам, используя понятный нейминг и минимизируя сложность логики внутри команд.
ссылка на оригинал статьи https://habr.com/ru/articles/930600/
Добавить комментарий