Встроенный retry механизм

Cypress предоставляет встроенный механизм повторных попыток (retry) для работы с асинхронными операциями и нестабильными элементами интерфейса. Этот механизм позволяет автоматизировать повторные проверки до достижения нужного состояния, что значительно упрощает тестирование динамических веб-приложений.

Автоматическое повторение команд

Все команды Cypress, такие как cy.get(), cy.contains(), cy.click() и другие, поддерживают автоматическое повторение до выполнения условия. Если команда не находит элемент или не выполняется условие, Cypress продолжает пробовать в течение времени, заданного настройкой defaultCommandTimeout.

Пример:

cy.get('.notification')
  .should('be.visible')
  .and('contain', 'Успешно сохранено');

В этом примере Cypress автоматически повторяет поиск элемента .notification до того момента, пока элемент не станет видимым и не будет содержать текст Успешно сохранено. Если условие не выполняется в течение defaultCommandTimeout (по умолчанию 4000 мс), тест завершается с ошибкой.

Повторение утверждений (.should() и .and())

Метод .should() интегрирован с retry механизмом. В отличие от обычных утверждений в JavaScript, Cypress повторяет выполнение до тех пор, пока проверка не пройдет или не истечет таймаут.

Пример проверки атрибутов:

cy.get('input[name="email"]')
  .should('have.value', 'user@example.com');

Даже если значение input изменяется динамически через JavaScript после загрузки страницы, Cypress будет повторять проверку до получения ожидаемого результата.

Настройка таймаутов для retry

Для каждой команды можно настроить отдельный таймаут с помощью опции timeout. Это особенно полезно при работе с элементами, которые загружаются дольше обычного.

cy.get('.slow-loading-element', { timeout: 10000 })
  .should('be.visible');

В данном примере Cypress будет повторно искать элемент до 10 секунд, прежде чем завершить тест с ошибкой.

Retry при взаимодействии с элементами

Механизм retry распространяется на команды взаимодействия, такие как click(), type(), select(). Cypress повторяет попытки до тех пор, пока элемент не станет доступен для действия.

Пример:

cy.get('button.submit')
  .click();

Если кнопка .submit еще не доступна для клика (например, из-за анимации или блокировки), Cypress повторит попытку до истечения таймаута, что исключает необходимость использования wait() в большинстве случаев.

Retry при сетевых запросах

Cypress позволяет использовать retry не только для DOM-элементов, но и для сетевых запросов через cy.intercept() и cy.wait(). Проверка статуса ответа или содержимого данных также поддерживает повторные попытки.

cy.intercept('GET', '/api/data').as('getData');
cy.visit('/dashboard');
cy.wait('@getData').its('response.statusCode').should('eq', 200);

Если запрос еще не завершен или возвращает другой статус, Cypress повторно проверяет условие до достижения таймаута.

Повторные попытки в кастомных командах

Создавая кастомные команды через Cypress.Commands.add, можно использовать встроенный retry механизм, возвращая Cypress chainable объекты.

Пример:

Cypress.Commands.add('login', (username, password) => {
  cy.get('input[name="username"]').type(username);
  cy.get('input[name="password"]').type(password);
  cy.get('button.login').click();
  cy.get('.dashboard').should('be.visible');
});

Даже внутри кастомной команды все .get() и .should() будут использовать retry, что повышает надежность тестов.

Ограничения retry

  • Механизм работает только с командами Cypress, а не с чистым JavaScript.
  • Retry применяется к командам, которые возвращают chainable объект. Утверждения вне цепочки (expect без should) повторяться не будут.
  • Механизм повторяет проверку только до истечения таймаута, после чего тест завершится с ошибкой.

Настройка глобального поведения retry

Можно настроить глобальные таймауты для всех команд и assertions в cypress.config.js:

export default {
  e2e: {
    defaultCommandTimeout: 8000,
    responseTimeout: 10000,
  }
}

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

Важность встроенного retry

Retry механизм исключает использование произвольных пауз (cy.wait(2000)), делает тесты более стабильными и сокращает вероятность ложных срабатываний. Он обеспечивает синхронизацию с реальной динамикой приложения и позволяет писать лаконичные и надежные сценарии тестирования.