Retry-ability механизм

Retry-ability — одна из ключевых особенностей Cypress, которая отличает его от других инструментов тестирования и делает автоматизацию стабильной и предсказуемой. Этот механизм позволяет тестам повторно проверять действия и утверждения до тех пор, пока они не выполнятся успешно или не истечет таймаут. Понимание его работы критично для эффективного написания тестов в реальных приложениях с асинхронным поведением.


Основы Retry-ability

В Cypress каждый командный вызов автоматически повторяется до тех пор, пока:

  • Утверждение (assertion) не выполнится успешно.
  • Не закончится таймаут команды (defaultCommandTimeout, стандартно 4 секунды).

Механизм работает только на уровне команд и утверждений, а не произвольного кода внутри then или JavaScript-функций. Это позволяет тестам быть устойчивыми к динамическим изменениям DOM, задержкам сети и асинхронной загрузке данных.

Пример базового использования:

cy.get('button.submit').should('be.visible').click();

В этом коде cy.get('button.submit') будет повторяться до тех пор, пока элемент не станет доступен в DOM и видимым. Аналогично, should('be.visible') проверка повторяется, что предотвращает падение теста из-за кратковременной задержки рендеринга кнопки.


Команды, поддерживающие Retry-ability

Не все действия в Cypress повторяются автоматически. Ключевые команды с поддержкой механизма:

  • Навигация и поиск элементов: cy.get(), cy.contains(), cy.find()
  • Утверждения: should(), and()
  • Взаимодействие с элементами: click(), type(), check() при использовании совместно с should()

Пример использования should() с retry:

cy.get('.notification')
  .should('have.text', 'Успешно сохранено');

Cypress будет повторять проверку текста .notification до тех пор, пока содержимое не станет 'Успешно сохранено' или не закончится таймаут.


Настройка таймаутов

Каждая команда и утверждение имеют таймаут, после которого retry прекращается:

  • Глобальный таймаут для всех команд: Cypress.config('defaultCommandTimeout', 8000)
  • Локальный таймаут для конкретной команды:
cy.get('.loader', { timeout: 10000 }).should('not.exist');
  • Утверждения также можно настраивать через {timeout: ...}:
cy.get('.status', { timeout: 5000 }).should('contain', 'Готово');

Умелое управление таймаутами повышает стабильность тестов, особенно при проверке динамически изменяющихся элементов.


Retry-ability и асинхронные действия

Retry-ability особенно полезна при тестировании приложений с асинхронной загрузкой данных, например:

  • Получение данных через API
  • Анимации и динамические изменения DOM
  • Ленивая загрузка компонентов

Пример для асинхронного запроса:

cy.intercept('GET', '/api/users').as('getUsers');
cy.visit('/users');
cy.wait('@getUsers');
cy.get('.user-list li').should('have.length.greaterThan', 0);

Здесь .should('have.length.greaterThan', 0) будет повторяться до тех пор, пока список пользователей не загрузится, несмотря на возможные задержки сети.


Retry-ability и цепочки команд

Важно понимать, что retry работает на уровне каждой команды, а не всей цепочки. Если цепочка состоит из нескольких команд, каждая проверка повторяется отдельно:

cy.get('.form')
  .find('input[name="email"]')
  .should('be.visible')
  .type('test@example.com')
  .should('have.value', 'test@example.com');
  • cy.get('.form') повторяется до появления формы
  • find('input[name="email"]') повторяется пока элемент не станет видимым
  • should('have.value', ...) повторяется после ввода текста, пока значение поля не совпадет

Ограничения механизма

Несмотря на удобство, retry-ability имеет ограничения:

  1. Функции внутри then() или обычного JavaScript-кода не повторяются автоматически:
cy.get('button').then($btn => {
  if ($btn.text() === 'Submit') {
    // Этот код выполнится только один раз, retry не применяется
  }
});
  1. Команды без утверждений не используют retry. Например, cy.log() или cy.visit() выполняются один раз.

  2. Сложные условия на несколько элементов могут потребовать явного использования should или вспомогательных функций.


Рекомендации по использованию

  • Использовать assertions для всех динамических проверок. Это обеспечивает автоматический retry.
  • Избегать сложной логики внутри then(), если она должна повторяться — лучше вынести в should.
  • Настраивать таймауты разумно — слишком короткий таймаут вызывает ложные падения, слишком длинный — увеличивает время тестов.
  • Комбинировать с intercept и wait для стабильного тестирования асинхронных API-запросов.

Механизм retry-ability в Cypress упрощает тестирование асинхронных приложений, повышает устойчивость тестов и снижает вероятность ложных падений. Грамотное использование should, таймаутов и правильной цепочки команд позволяет создавать надежные и предсказуемые тестовые сценарии.