Control flow: управление потоком выполнения

Protractor — это инструмент для автоматизированного тестирования веб-приложений на Angular и не только. Одной из ключевых особенностей при работе с Protractor является управление потоком выполнения асинхронных операций. Важно понимать, что тесты в Protractor часто взаимодействуют с элементами страницы, выполнение которых требует времени, поэтому правильное управление асинхронностью критично для стабильности тестов.


Асинхронность и Promise

Protractor использует Promise для работы с асинхронными операциями, такими как поиск элементов или выполнение действий над ними. Каждое взаимодействие с элементом возвращает Promise, который разрешается после завершения операции. Например:

let button = element(by.id('submit'));
button.click(); // возвращает Promise

Если попытаться обратиться к результату асинхронной операции напрямую, без ожидания завершения Promise, тест может завершиться с ошибкой. Для управления этим применяются конструкции then, async/await.


Использование then для управления потоком

Метод then позволяет выполнять действия после завершения предыдущей асинхронной операции:

element(by.id('username')).sendKeys('admin').then(function() {
    return element(by.id('password')).sendKeys('1234');
}).then(function() {
    return element(by.id('login')).click();
});

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


async / await — современный подход

С версии Protractor 5+ и с внедрением Node.js с поддержкой async/await появилась возможность писать тесты, которые выглядят синхронно, хотя остаются асинхронными:

it('должен залогиниться', async () => {
    await element(by.id('username')).sendKeys('admin');
    await element(by.id('password')).sendKeys('1234');
    await element(by.id('login')).click();
});

Преимущества:

  • Читаемость кода значительно выше.
  • Меньше вложенных колбэков.
  • Легче отлавливать ошибки с использованием try/catch.

Контроль ожиданий: browser.wait

Для корректного управления потоком выполнения часто требуется явное ожидание определённых условий. Для этого используется browser.wait:

let EC = protractor.ExpectedConditions;
let loginButton = element(by.id('login'));

await browser.wait(EC.elementToBeClickable(loginButton), 5000);
await loginButton.click();

Ключевые моменты:

  • ExpectedConditions содержит набор готовых условий: видимость, кликабельность, наличие текста.
  • Таймаут указывается в миллисекундах.
  • Без явного ожидания клики и взаимодействия могут выполняться до загрузки элемента.

Последовательное выполнение действий над элементами

Когда нужно выполнить серию действий над множеством элементов, следует использовать цикл с await:

let items = element.all(by.css('.todo-item'));

for (let i = 0; i < await items.count(); i++) {
    let item = items.get(i);
    await item.click();
}

Важно:

  • Метод count() также возвращает Promise, поэтому перед циклом используется await.
  • Каждый элемент обрабатывается последовательно, что исключает гонку состояний.

Параллельное выполнение операций

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

let usernameField = element(by.id('username')).sendKeys('admin');
let passwordField = element(by.id('password')).sendKeys('1234');

await Promise.all([usernameField, passwordField]);
await element(by.id('login')).click();

Применяется, когда операции не зависят друг от друга. Это ускоряет тесты, но требует внимательного контроля состояния страницы.


Ошибки управления потоком

Частые ошибки при работе с асинхронностью:

  • Попытка доступа к элементу до его появления на странице.
  • Неправильное ожидание, приводящее к TimeoutError.
  • Непоследовательное выполнение действий над элементами списка.

Использование async/await совместно с browser.wait и ExpectedConditions минимизирует такие ошибки.


Встроенный контроль потока в Protractor

Ранее Protractor имел WebDriver Control Flow, который автоматически управлял Promise. Начиная с версии 6+, Control Flow отключён, и теперь все тесты требуют явного управления асинхронностью с использованием async/await. Это повышает прозрачность выполнения тестов и упрощает отладку.


Советы по стабильному управлению потоком

  1. Явное ожидание элементов: всегда использовать browser.wait или ExpectedConditions.
  2. Последовательные действия: использовать await для каждого действия, если оно зависит от предыдущего.
  3. Параллельные действия только при независимости: использовать Promise.all.
  4. Обработка ошибок: оборачивать критические действия в try/catch.
  5. Избегать старого Control Flow: современные версии Protractor требуют явной асинхронности, что делает код предсказуемым.

Контроль потока выполнения в Protractor — это фундаментальный навык, обеспечивающий корректность тестов и их стабильность. Понимание Promise, правильное применение async/await и явных ожиданий позволяет создавать надёжные автоматизированные сценарии для любых веб-приложений.