Создание собственных команд

Cypress предоставляет мощный механизм расширения стандартного набора команд с помощью пользовательских команд. Они позволяют уменьшить дублирование кода, повысить читаемость тестов и инкапсулировать сложные последовательности действий в одну удобную функцию.


Регистрация пользовательской команды

Для создания собственной команды используется функция Cypress.Commands.add. Она принимает два обязательных аргумента: название команды и функцию, реализующую её поведение.

Cypress.Commands.add('login', (username, password) => {
  cy.get('#username').type(username)
  cy.get('#password').type(password)
  cy.get('button[type="submit"]').click()
})

После добавления команды она становится доступной в любом тесте:

cy.login('admin', '12345')

Особенности:

  • Название команды должно быть уникальным, чтобы не конфликтовать с встроенными командами Cypress.
  • Функция может возвращать цепочку команд Cypress, что позволяет продолжать использование cy после вызова пользовательской команды.

Создание команд с возвратом значения

Иногда необходимо, чтобы команда возвращала результат выполнения. Для этого используется return в функции команды:

Cypress.Commands.add('getUserData', (userId) => {
  return cy.request(`/api/users/${userId}`).then((response) => {
    return response.body
  })
})

cy.getUserData(5).then((user) => {
  expect(user.name).to.eq('John Doe')
})

Ключевые моменты:

  • Команды Cypress всегда работают асинхронно и используют цепочку промисов.
  • Возврат значения через .then() позволяет обрабатывать результат внутри теста.

Команды с опциями

Пользовательские команды могут принимать объект опций для настройки поведения:

Cypress.Commands.add('fillForm', (data, options = {}) => {
  const { clearFields = true } = options

  if (clearFields) {
    cy.get('#name').clear()
    cy.get('#email').clear()
  }

  cy.get('#name').type(data.name)
  cy.get('#email').type(data.email)
})

Применение:

cy.fillForm({ name: 'Alice', email: 'alice@example.com' }, { clearFields: false })

Преимущество: упрощает создание гибких команд, которые можно использовать в разных сценариях без дублирования кода.


Команды с асинхронными операциями

Пользовательские команды могут включать асинхронные запросы, работу с API или таймеры:

Cypress.Commands.add('waitForApi', (endpoint) => {
  cy.intercept(endpoint).as('apiCall')
  cy.wait('@apiCall').then((interception) => {
    expect(interception.response.statusCode).to.eq(200)
  })
})

Использование в тесте:

cy.waitForApi('/api/data')

Важный момент: использование cy.intercept и cy.wait внутри команды позволяет полностью инкапсулировать логику проверки сетевых запросов.


Создание цепочек команд

Пользовательские команды могут быть созданы так, чтобы поддерживать цепочки Cypress, возвращая cy:

Cypress.Commands.add('selectDropdown', (selector, value) => {
  return cy.get(selector).select(value)
})

Цепочка:

cy.selectDropdown('#country', 'USA').get('#submit').click()

Особенность: возвращение cy обеспечивает совместимость с другими командами и позволяет строить читаемые тестовые сценарии.


Команды с родительским контекстом

Cypress поддерживает создание команд, привязанных к конкретному элементу DOM, через Cypress.Commands.add с третьим параметром { prevSubject: true }. Это полезно для расширения функционала стандартных команд:

Cypress.Commands.add('highlight', { prevSubject: true }, (subject) => {
  subject.css('border', '2px solid red')
  return subject
})

Применение:

cy.get('#username').highlight().type('admin')

Примечание: prevSubject: true позволяет использовать команду на уже выбранном элементе и возвращать его для дальнейших действий.


Организация пользовательских команд

  • Все пользовательские команды рекомендуется хранить в файле cypress/support/commands.js.
  • Можно разделять команды по категориям, создавая отдельные модули и импортируя их в commands.js.
  • Использование команд повышает читаемость тестов и облегчает поддержку больших проектов.

Практические рекомендации

  1. Инкапсулировать повторяющиеся последовательности действий в команды (login, fillForm, navigateTo).
  2. Использовать цепочки cy для интеграции с другими Cypress-командами.
  3. Проверять возвращаемые значения через .then() для асинхронных операций.
  4. Добавлять опции к командам для гибкости и масштабируемости.
  5. Придерживаться единообразного именования команд для удобства командной работы.

Эффективное использование пользовательских команд делает тесты Cypress более компактными, структурированными и легко расширяемыми, позволяя создавать сложные сценарии с минимальным дублированием кода.