Timeout и reject обработка

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


Асинхронная модель Protractor

Protractor использует WebDriverJS, который построен на промисах. Любое действие с элементом страницы, будь то поиск, клик или получение текста, возвращает объект Promise. Асинхронная модель позволяет тестам не блокировать поток выполнения, но требует правильного управления таймаутами и обработкой ошибок.

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

  • Любой промис может быть отклонён (reject) при ошибках взаимодействия, например, элемент не найден или истёк таймаут.
  • Методы Protractor, такие как element(locator).click() или element(locator).getText(), не выбрасывают исключение напрямую, а возвращают промис, который может быть rejected.

Управление таймаутами

Protractor предоставляет несколько уровней таймаутов:

  1. Implicit Waits (неявные ожидания) Устанавливаются через browser.manage().timeouts().implicitlyWait(ms). Пример:

    browser.manage().timeouts().implicitlyWait(5000); // 5 секунд

    Особенности:

    • Подходит для глобального ожидания элементов.
    • Все методы поиска элементов будут ждать указанное время перед reject.
  2. Explicit Waits (явные ожидания) Реализуются через ExpectedConditions:

    const EC = protractor.ExpectedConditions;
    const elem = element(by.id('submit'));
    
    browser.wait(EC.elementToBeClickable(elem), 10000, 'Элемент не кликабелен');

    Особенности:

    • Контролирует конкретные условия (видимость, кликабельность, наличие текста).
    • Позволяет задать собственное сообщение об ошибке.
    • Таймаут можно задать индивидуально для каждого ожидания.
  3. Page Load Timeout и Script Timeout Настройки браузера через browser.manage().timeouts():

    browser.manage().timeouts().pageLoadTimeout(20000); // Таймаут загрузки страницы
    browser.manage().timeouts().setScriptTimeout(15000); // Таймаут выполнения скрипта
    • pageLoadTimeout контролирует время ожидания полной загрузки страницы.
    • setScriptTimeout применяется при выполнении асинхронного JavaScript через browser.executeAsyncScript().

Обработка reject промисов

Reject промиса возникает в случаях:

  • Элемент не найден (NoSuchElementError)
  • Элемент не кликабелен (ElementNotInteractableError)
  • Превышен таймаут ожидания (TimeoutError)
  • Ошибка выполнения скрипта в браузере

Правильная обработка reject критична для предотвращения падений тестов и получения информативных логов.

Пример с .then() и .catch():

element(by.id('submit')).click()
  .then(() => {
      console.log('Клик выполнен успешно');
  })
  .catch(err => {
      if (err.name === 'TimeoutError') {
          console.error('Элемент не появился вовремя');
      } else {
          console.error('Ошибка при клике:', err);
      }
  });

Использование async/await с try/catch:

async function clickSubmit() {
    const submitBtn = element(by.id('submit'));
    try {
        await browser.wait(protractor.ExpectedConditions.elementToBeClickable(submitBtn), 10000);
        await submitBtn.click();
        console.log('Клик успешен');
    } catch (err) {
        if (err.name === 'TimeoutError') {
            console.error('Элемент не стал кликабельным вовремя');
        } else {
            console.error('Ошибка клика:', err);
        }
    }
}
  • async/await позволяет писать асинхронный код линейно, улучшая читаемость.
  • try/catch гарантирует обработку всех rejected промисов.

Практические рекомендации

  1. Не использовать только implicit wait Неявные ожидания могут замедлять тесты и скрывать реальные проблемы. Предпочтительнее комбинировать явные ожидания для ключевых элементов.

  2. Использовать специфичные ExpectedConditions Вместо presenceOf использовать visibilityOf или elementToBeClickable для операций, требующих взаимодействия.

  3. Оборачивать действия с элементами в try/catch Даже при использовании явных ожиданий возможны ошибки. Это помогает создавать устойчивые тесты.

  4. Использовать кастомные таймауты для критичных шагов Для сложных страниц или динамических элементов стандартный таймаут 5–10 секунд может быть недостаточным.

  5. Логировать причины reject Всегда фиксировать тип ошибки (err.name) и сообщение (err.message) для упрощения отладки.


Таймауты в конфигурации Protractor

В protractor.conf.js можно задать глобальные таймауты:

exports.config = {
    allScriptsTimeout: 11000, // Максимальное время выполнения скриптов
    getPageTimeout: 10000,    // Таймаут загрузки страницы
    jasmineNodeOpts: {
        defaultTimeoutInterval: 30000 // Таймаут для тестов
    }
};
  • allScriptsTimeout — контролирует выполнение асинхронных скриптов, используемых в browser.executeAsyncScript().
  • getPageTimeout — максимальное время загрузки каждой страницы.
  • defaultTimeoutInterval — таймаут ожидания завершения каждого it блока в Jasmine.

Обработка цепочек промисов

В Protractor часто используются цепочки промисов:

element(by.id('input')).sendKeys('test')
  .then(() => element(by.id('submit')).click())
  .then(() => element(by.id('result')).getText())
  .then(text => console.log('Результат:', text))
  .catch(err => console.error('Ошибка в цепочке:', err));
  • Любой reject на любом шаге передаст управление в блок catch.
  • Можно использовать Promise.all() для параллельных операций с элементами и обрабатывать их общий reject:
Promise.all([
    element(by.id('input1')).sendKeys('a'),
    element(by.id('input2')).sendKeys('b')
])
.then(() => console.log('Оба ввода выполнены'))
.catch(err => console.error('Ошибка при вводе:', err));

Подводные камни

  • Таймауты ожидания не являются гарантией успеха клика или ввода. Элемент может стать кликабельным на момент проверки, но затем исчезнуть.
  • Необработанные reject промисы приводят к падению теста без информативного сообщения. Всегда использовать catch или try/catch.
  • Смешивание implicit и explicit waits может вызвать непредсказуемое поведение. Явные ожидания предпочтительнее для стабильных тестов.

Тщательное управление таймаутами и корректная обработка reject промисов является основой устойчивого и предсказуемого тестирования с Protractor. Правильная настройка ожиданий, комбинирование explicit waits и обработка ошибок в цепочках промисов позволяет создавать надежные скрипты для сложных веб-приложений.