Асинхронность является одной из ключевых особенностей веб-приложений. Cypress построен на синхронной модели команд, но под капотом каждая операция может быть асинхронной. Понимание того, как Cypress обрабатывает асинхронные действия, позволяет создавать надёжные и стабильные тесты.
Cypress использует очередь команд (command queue).
Каждая команда, например cy.get(), cy.click(),
cy.request(), добавляется в очередь и выполняется
последовательно, несмотря на асинхронность внутренних
операций браузера.
Пример:
cy.get('button').click();
cy.get('input').type('Cypress');
cy.get('.result').should('contain', 'Success');
Даже если cy.get('button') требует времени для поиска
элемента, следующая команда cy.get('input') не выполнится
до завершения предыдущей.
Особенность: нельзя напрямую использовать промисы JavaScript без специальных подходов, так как Cypress ожидает работу через свою цепочку команд.
then() для асинхронных данныхМетод then() позволяет получить результат асинхронной
операции и использовать его в тесте:
cy.get('input').then(($input) => {
const value = $input.val();
expect(value).to.equal('default');
});
$input — это объект jQuery, возвращаемый Cypress.then() выполняются после
того, как команда cy.get() завершила поиск.then() также может использоваться для возврата значений
и передачи их в цепочку:
cy.get('input').invoke('val').then((text) => {
cy.get('.output').should('contain', text);
});
cy.wrap() для
интеграции с промисамиКогда нужно работать с обычными JavaScript промисами, используется
cy.wrap():
const fetchData = () => Promise.resolve('data');
cy.wrap(fetchData()).then((result) => {
expect(result).to.equal('data');
});
cy.wrap() преобразует промис в команду Cypress, добавляя
его в очередь. Без wrap цепочка команд Cypress может не
дождаться результата промиса.
Cypress автоматически ожидает элементы и состояния с помощью встроенных таймаутов.
Пример:
cy.get('.loading', { timeout: 10000 }).should('not.exist');
.loading.setTimeout или
ручные задержки.should() и and() выполняются
многократно до того момента, пока условие не станет
истинным или не закончится таймаут. Это делает тесты более устойчивыми к
асинхронным изменениям на странице.
Cypress предоставляет cy.request() для работы с
HTTP-запросами:
cy.request('/api/data')
.then((response) => {
expect(response.status).to.eq(200);
expect(response.body).to.have.property('items');
});
cy.wait() для синхронизации
с сетевыми запросами:cy.intercept('/api/data').as('getData');
cy.visit('/dashboard');
cy.wait('@getData').its('response.statusCode').should('eq', 200);
Использование стандартного цикла forEach для асинхронных
операций в Cypress может привести к неожиданным результатам. Правильный
подход — использовать цепочку команд:
const items = ['A', 'B', 'C'];
items.forEach((item) => {
cy.get('input').type(item).should('have.value', item);
});
Если нужно асинхронно обработать данные через промис, следует
комбинировать с cy.wrap:
items.reduce((prev, item) => {
return prev.then(() => cy.wrap(Promise.resolve(item)).then((val) => {
cy.get('input').type(val).should('have.value', val);
}));
}, Cypress.Promise.resolve());
cy.wrap или then.async/await внутри Cypress
цепочек, так как это может нарушить последовательность
выполнения команд.cy.wait() для
синхронизации с асинхронными событиями (AJAX, WebSocket, таймеры).should предпочтительнее
прямых проверок в then, так как should
повторяется до успешного выполнения.Асинхронная обработка в Cypress строится на принципах очереди
команд, ожидания состояния элементов и
интеграции промисов через wrap. Понимание
этих механизмов позволяет создавать стабильные и предсказуемые тесты,
которые не ломаются при изменениях скорости работы приложения или
задержках сети.