В Angular-приложениях взаимодействие с асинхронными операциями
встречается повсеместно: HTTP-запросы, таймеры, обработка событий,
обращение к локальному хранилищу и многое другое. В контексте
автоматизированного тестирования с использованием Protractor правильная
работа с Promise критически важна для стабильности и
предсказуемости тестов.
Promise — это объект, представляющий результат
асинхронной операции, который может находиться в одном из трёх
состояний:
Создание промиса выполняется через конструктор:
let promise = new Promise((resolve, reject) => {
// Асинхронная операция
if (успех) {
resolve(result);
} else {
reject(error);
}
});
Обработка результата осуществляется с помощью методов
.then() и .catch():
promise
.then(result => console.log('Результат:', result))
.catch(error => console.error('Ошибка:', error));
Protractor — это инструмент для end-to-end тестирования
Angular-приложений, построенный поверх WebDriverJS. Он использует
Promise для работы с асинхронными действиями браузера.
Все методы Protractor, возвращающие элементы или выполняющие действия с ними, возвращают промисы. Например:
let button = element(by.css('.submit-button'));
button.click(); // Возвращает Promise
Для корректного выполнения последовательности действий промисы необходимо обрабатывать:
button.click()
.then(() => {
return element(by.css('.result')).getText();
})
.then(text => {
console.log('Текст после клика:', text);
});
async/awaitИспользование async/await позволяет писать
асинхронный код в стиле синхронного, что делает тесты более
читаемыми:
it('должен получить текст после клика', async () => {
let button = element(by.css('.submit-button'));
await button.click();
let result = await element(by.css('.result')).getText();
console.log('Результат:', result);
});
Ключевой момент: await работает только
с объектами, реализующими интерфейс Promise. Все методы
Protractor можно использовать с await.
Angular активно использует зону (zone.js) для
отслеживания асинхронных операций. Protractor автоматически ждет
завершения всех задач Angular перед выполнением следующего шага, что
снижает вероятность «гонок» (race conditions).
Однако существуют ситуации, когда ожидание нужно реализовать вручную:
Для этого применяются Explicit Waits:
let EC = protractor.ExpectedConditions;
let elementToWait = element(by.css('.dynamic-element'));
browser.wait(EC.visibilityOf(elementToWait), 5000, 'Элемент не появился');
Метод browser.wait возвращает Promise,
который резолвится после выполнения условия или отклоняется по
таймауту.
Часто в тестах возникает необходимость выполнять несколько асинхронных операций:
Последовательно:
await element(by.css('.input')).sendKeys('тест');
await element(by.css('.submit')).click();
let text = await element(by.css('.result')).getText();
Параллельно с Promise.all:
let elements = [element1.getText(), element2.getText(), element3.getText()];
let results = await Promise.all(elements);
console.log('Результаты всех элементов:', results);
Promise.all завершится, когда все промисы будут
выполнены. Если один промис отклоняется, весь Promise.all
будет отклонён.
Ошибки асинхронных операций нужно обрабатывать, иначе тест может завершиться неконтролируемым исключением.
Использование try/catch с
async/await:
try {
await element(by.css('.submit')).click();
let text = await element(by.css('.result')).getText();
console.log(text);
} catch (error) {
console.error('Ошибка при выполнении теста:', error);
}
При цепочках .then() ошибки можно ловить через
.catch():
element(by.css('.submit')).click()
.then(() => element(by.css('.result')).getText())
.then(text => console.log(text))
.catch(error => console.error('Ошибка:', error));
Protractor предоставляет специальный метод
browser.waitForAngular() для ручного ожидания завершения
всех асинхронных операций Angular:
await browser.waitForAngular();
let data = await element(by.css('.data')).getText();
Это особенно важно при тестировании страниц, где Angular-операции инициируются динамически или после AJAX-запросов.
async/await вместо
цепочек .then(), если тест содержит более двух шагов.map с
Promise.all, чтобы собирать результаты одновременно.sleep без необходимости;
предпочтительнее ExpectedConditions..then(console.log) перед основным действием.Начиная с Protractor 7 и выше, управление контролом синхронизации
стало более явным. Автоматическое ожидание Angular
(control flow) отключено, что делает
async/await обязательным для большинства
тестов.
browser.waitForAngularEnabled(false); // для не-Angular страниц
Это важно учитывать при интеграции тестов с приложениями, где часть страниц не управляется Angular.
Работа с промисами в Angular и Protractor требует аккуратного
обращения с асинхронностью, грамотного использования
async/await и контроля над ожиданиями.
Понимание этих принципов обеспечивает стабильные и предсказуемые
end-to-end тесты, минимизируя ошибки синхронизации и «гонок» между
шагами тестов.