Cypress построен на принципах асинхронного выполнения команд, что делает работу с промисами одной из ключевых концепций при автоматизации тестирования на JavaScript. Несмотря на то, что Cypress скрывает большую часть асинхронности под цепочкой команд, понимание промисов необходимо для корректного написания сложных сценариев.
Каждая команда Cypress, например cy.get(),
cy.click() или cy.request(), выполняется
асинхронно и возвращает объект цепочки команд, а не сам результат
напрямую. Это значит, что попытка получить результат сразу после вызова
метода приведет к неожиданным результатам.
Пример неправильного подхода:
const element = cy.get('#submit-button');
console.log(element); // Это не HTMLElement, а объект цепочки команд Cypress
Правильный подход — использование встроенной функции
.then(), которая принимает колбэк и передает результат
после завершения команды:
cy.get('#submit-button').then(($button) => {
console.log($button.text()); // Получаем текст кнопки после её поиска в DOM
});
Иногда требуется работать с внешними промисами, например с API или асинхронными функциями JavaScript. В таких случаях Cypress предоставляет возможность интеграции стандартных промисов с цепочкой команд.
function fetchData() {
return fetch('https://api.example.com/data')
.then(response => response.json());
}
cy.wrap(fetchData()).then((data) => {
expect(data).to.have.property('id');
});
Метод cy.wrap() позволяет обернуть любой промис или
значение и добавить его в цепочку Cypress. Это обеспечивает
синхронизацию асинхронных действий и правильное управление временем
ожидания.
Cypress автоматически обрабатывает ошибки в своих командах, но при
работе с внешними промисами важно учитывать возможные исключения. Для
этого используют .catch() или встроенную конструкцию
cy.wrap() с обработкой ошибок:
cy.wrap(fetchData())
.then((data) => {
expect(data).to.have.property('name');
})
.catch((err) => {
throw new Error('Ошибка при загрузке данных: ' + err.message);
});
Для выполнения нескольких асинхронных действий последовательно важно
помнить, что Cypress формирует очередь команд, а не выполняет их сразу.
Любые асинхронные функции внутри .then() будут ждать
завершения текущей команды:
cy.get('#input')
.type('Тест')
.then(() => {
return cy.get('#submit').click();
})
.then(() => {
cy.get('#result').should('contain', 'Успех');
});
В этом примере каждая команда выполняется только после успешного завершения предыдущей, что исключает гонки данных и ошибки синхронизации.
async/await с CypressПрямое использование async/await с командами Cypress не
рекомендуется, так как команды возвращают цепочку Cypress, а не промис.
Однако, при работе с внешними асинхронными функциями допустимо:
async function getData() {
const response = await fetch('https://api.example.com/data');
return response.json();
}
cy.wrap(getData()).then((data) => {
expect(data).to.have.property('status', 'ok');
});
Важно сохранять разделение между Cypress-командами и внешними промисами, чтобы не нарушать внутренний механизм очереди.
cy.wrap()..then() для обработки результатов
Cypress команд, а не ожидать синхронный результат.cy.wrap(), чтобы сохранить корректное поведение
таймаутов и ожиданий.async/await внутри Cypress
цепочек команд, чтобы не нарушить последовательность
выполнения.cy.request('GET', '/users/1')
.then((response) => {
expect(response.status).to.eq(200);
cy.get('#username').type(response.body.name);
});
cy.wrap(fetchData())
.then((user) => {
return fetchUserPosts(user.id);
})
.then((posts) => {
expect(posts).to.have.length.greaterThan(0);
});
cy.wrap(fetchData())
.then((data) => {
if (!data.success) {
throw new Error('Не удалось получить данные');
}
cy.get('#output').text(data.result);
});
Понимание работы с промисами в Cypress является основой для создания надёжных и предсказуемых тестов, особенно при сложных сценариях, включающих взаимодействие с API, асинхронную загрузку данных и последовательное выполнение действий в DOM.