Обработка множественных асинхронных операций

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

Важность асинхронного подхода

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

Механизмы синхронизации в WebdriverIO

Для эффективной работы с множественными асинхронными операциями WebdriverIO предоставляет несколько механизмов синхронизации. Они позволяют гарантировать, что тест будет выполнен корректно, даже если операции выполняются в разное время или с задержками.

Ожидания (Waits)

Ожидания играют важную роль в работе с асинхронными операциями. Например, чтобы дождаться появления элемента на странице или изменения его состояния, можно использовать встроенные команды ожидания. WebdriverIO поддерживает явные и неявные ожидания.

  • Неявные ожидания (Implicit Waits): Эти ожидания устанавливаются для всего теста и действуют для всех команд, которые ожидают появления элемента. Время ожидания устанавливается один раз, и если элемент не появляется в это время, возникает ошибка.
browser.setTimeout({ 'implicit': 10000 }); // Устанавливает неявное ожидание на 10 секунд
  • Явные ожидания (Explicit Waits): Явные ожидания позволяют указать конкретные условия для каждого действия. Например, можно ожидать, пока элемент не станет видимым на странице.
const element = $('#elementId');
element.waitForDisplayed({ timeout: 5000 }); // Явное ожидание появления элемента на странице

Асинхронная обработка с помощью async/await

WebdriverIO использует async/await для упрощения работы с асинхронными операциями. Все основные команды WebdriverIO возвращают промисы, которые можно ожидать с помощью ключевого слова await. Это позволяет писать код, который выглядит синхронно, несмотря на то, что выполняется асинхронно.

Пример:

it('должен подождать появления кнопки и кликнуть её', async () => {
  const button = await $('#submitButton');
  await button.waitForDisplayed();
  await button.click();
});

Использование await позволяет избежать проблем с вложенностью и делает код чище.

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

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

Для параллельного выполнения задач можно использовать Promise.all(). Этот метод позволяет запустить несколько асинхронных операций одновременно и дождаться их завершения.

Пример параллельного выполнения двух команд:

it('должен подождать загрузки элементов и выполнить действия', async () => {
  const element1 = $('#element1');
  const element2 = $('#element2');

  await Promise.all([
    element1.waitForDisplayed(),
    element2.waitForDisplayed(),
  ]);

  await element1.click();
  await element2.click();
});

В данном примере оба элемента будут ожидаться одновременно, а после их появления можно продолжить выполнение следующих шагов.

Использование Promise.allSettled и Promise.race

В случае, если нужно обработать несколько промисов, но не обязательно, чтобы все они завершились успешно, можно использовать методы Promise.allSettled() или Promise.race().

  • Promise.allSettled(): Ожидает завершения всех промисов, независимо от того, завершились ли они успешно или с ошибкой. Это полезно, если важно дождаться выполнения всех операций, но обработка ошибок не является критичной.
it('параллельное выполнение с обработкой всех промисов', async () => {
  const promises = [
    $('#element1').waitForDisplayed(),
    $('#element2').waitForDisplayed(),
  ];

  const results = await Promise.allSettled(promises);
  
  results.forEach(result => {
    if (result.status === 'rejected') {
      console.error('Операция не выполнена', result.reason);
    }
  });
});
  • Promise.race(): Ожидает завершения первого из переданных промисов. Это может быть полезно, если нужно дождаться только одного события, например, первого появления элемента или получения первого ответа от сервера.
it('первый элемент будет кликнут', async () => {
  const promises = [
    $('#element1').waitForDisplayed(),
    $('#element2').waitForDisplayed(),
  ];

  await Promise.race(promises);

  await $('#element1').click(); // Кликнуть на первый элемент, который появился
});

Обработка ошибок в асинхронных операциях

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

В случае использования async/await, ошибки можно обрабатывать с помощью блоков try/catch. Важно отметить, что команды WebdriverIO могут выбрасывать исключения, например, если элемент не найден или не появился в указанное время.

Пример обработки ошибок:

it('должен корректно обрабатывать ошибки', async () => {
  try {
    const element = await $('#nonexistentElement');
    await element.click();
  } catch (error) {
    console.error('Ошибка при взаимодействии с элементом:', error);
  }
});

Совмещение асинхронных операций и ожиданий

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

Пример комбинирования ожиданий и асинхронных операций:

it('должен сначала подождать, а затем кликнуть', async () => {
  const button = await $('#submitButton');
  await button.waitForDisplayed({ timeout: 5000 });

  await button.click();
  const confirmationMessage = await $('#confirmationMessage');
  await confirmationMessage.waitForDisplayed({ timeout: 3000 });
});

В этом примере сначала выполняется ожидание появления кнопки, затем она кликается, и после этого ожидается, что появится сообщение подтверждения.

Заключение

Грамотная организация работы с множественными асинхронными операциями в WebdriverIO требует правильного использования ожиданий, синхронизации и обработки ошибок. С помощью таких инструментов, как async/await, Promise.all(), Promise.race(), а также встроенных методов ожидания, можно эффективно управлять асинхронными процессами и создавать надёжные и стабильные тесты для веб-приложений.