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 будет повторять проверку до
получения ожидаемого результата.
Для каждой команды можно настроить отдельный таймаут с помощью опции
timeout. Это особенно полезно при работе с элементами,
которые загружаются дольше обычного.
cy.get('.slow-loading-element', { timeout: 10000 })
.should('be.visible');
В данном примере Cypress будет повторно искать элемент до 10 секунд, прежде чем завершить тест с ошибкой.
Механизм retry распространяется на команды взаимодействия, такие как
click(), type(), select().
Cypress повторяет попытки до тех пор, пока элемент не станет доступен
для действия.
Пример:
cy.get('button.submit')
.click();
Если кнопка .submit еще не доступна для клика (например,
из-за анимации или блокировки), Cypress повторит попытку до истечения
таймаута, что исключает необходимость использования wait()
в большинстве случаев.
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, что повышает надежность
тестов.
expect без should)
повторяться не будут.Можно настроить глобальные таймауты для всех команд и assertions в
cypress.config.js:
export default {
e2e: {
defaultCommandTimeout: 8000,
responseTimeout: 10000,
}
}
Это позволяет унифицировать retry и сократить необходимость указывать таймаут для каждой команды отдельно.
Retry механизм исключает использование произвольных пауз
(cy.wait(2000)), делает тесты более стабильными и сокращает
вероятность ложных срабатываний. Он обеспечивает синхронизацию с
реальной динамикой приложения и позволяет писать лаконичные и надежные
сценарии тестирования.