Error handling в асинхронном коде

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

Природа асинхронного кода в WebdriverIO

WebdriverIO использует промисы и async/await синтаксис для управления асинхронными операциями. Это позволяет писать код, который выглядит синхронно, но выполняется асинхронно. Например:

const result = await browser.getTitle();

В данном примере getTitle() является асинхронной операцией, которая возвращает заголовок страницы. Но благодаря await код не блокирует выполнение следующих строк, создавая иллюзию синхронного поведения.

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

Общие принципы обработки ошибок

В WebdriverIO можно использовать стандартные методы обработки ошибок в JavaScript. Один из самых распространенных подходов — это использование конструкции try...catch. Это позволяет перехватывать ошибки, возникшие в процессе выполнения асинхронных операций.

try {
    const title = await browser.getTitle();
    console.log(title);
} catch (error) {
    console.error('Ошибка при получении заголовка страницы:', error);
}

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

Обработка ошибок при взаимодействии с элементами

WebdriverIO предоставляет удобные методы для взаимодействия с элементами на странице, такие как click(), setValue(), getText(). Однако если элемент не будет найден или возникнет другая проблема (например, элемент не будет видим), можно столкнуться с ошибками. Для их обработки используется не только стандартный try...catch, но и возможности WebdriverIO для работы с исключениями.

try {
    await $('#button').click();
} catch (error) {
    console.error('Не удалось кликнуть по кнопке:', error);
}

Однако WebdriverIO позволяет избежать явной обработки ошибок в некоторых случаях через встроенные методы ожидания. Если элемент не найден, waitForExist() или waitForDisplayed() выбросят ошибку, но она будет являться частью ожидаемой логики, а не исключением.

const button = await $('#button');
await button.waitForExist({ timeout: 5000 });
await button.click();

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

Тайм-ауты и ожидания

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

try {
    await browser.waitUntil(async () => {
        const title = await browser.getTitle();
        return title === 'Expected Title';
    }, {
        timeout: 5000,
        timeoutMsg: 'Заголовок страницы не соответствует ожидаемому в течение 5 секунд.'
    });
} catch (error) {
    console.error('Ошибка ожидания заголовка:', error);
}

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

Работа с ошибками в цепочках промисов

WebdriverIO активно использует промисы для асинхронных операций, и ошибки, возникающие в промисах, можно обрабатывать с помощью метода .catch(). Это особенно полезно, если необходимо перехватывать ошибки в рамках цепочки вызовов.

browser
    .url('https://example.com')
    .getTitle()
    .then(title => {
        console.log(title);
    })
    .catch(error => {
        console.error('Ошибка при получении заголовка:', error);
    });

Здесь используется промис для выполнения последовательных операций: переход на сайт и получение заголовка страницы. Если на любом из этапов возникнет ошибка, она будет перехвачена в методе .catch().

Использование assert и expect для обработки ошибок

В WebdriverIO можно использовать ассерты для проверки условий в тестах. Ассерты позволяют не только проверять правильность результата, но и автоматически обрабатывать ошибки, если условие не выполняется. В WebdriverIO встроены библиотеки assert и expect (основанные на Chai).

Пример с использованием assert:

const title = await browser.getTitle();
assert.strictEqual(title, 'Expected Title', 'Заголовок страницы не совпадает с ожидаемым!');

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

Аналогично работает expect, предоставляя более читабельный синтаксис:

const title = await browser.getTitle();
expect(title).toBe('Expected Title');

В случае несоответствия будет выброшено исключение с описанием ошибки.

Логирование ошибок

Для более удобной диагностики ошибок, возникающих в процессе тестирования, полезно логировать информацию о них. В WebdriverIO доступна встроенная поддержка логирования через browser.logger или сторонние библиотеки, такие как winston.

Пример использования:

try {
    const element = await $('#element');
    await element.waitForExist();
} catch (error) {
    browser.logger.error('Ошибка при поиске элемента: ', error);
    throw error;  // Перебрасываем ошибку дальше
}

Логирование помогает в дальнейшем анализировать тесты, просматривая, где произошли сбои, что позволило бы улучшить диагностику и стабильность тестов.

Обработка ошибок при взаимодействии с сетевыми запросами

WebdriverIO также позволяет работать с сетевыми запросами, используя browser.call(). В случае проблем с запросами, например, тайм-аутов или недоступности ресурса, ошибки можно обработать аналогично асинхронному коду.

try {
    const response = await browser.call(async () => {
        return fetch('https://api.example.com/data');
    });
    const data = await response.json();
} catch (error) {
    console.error('Ошибка при запросе данных:', error);
}

Здесь используется fetch для отправки HTTP-запроса, а ошибка, возникающая в процессе запроса, обрабатывается стандартным методом catch.

Итоговые рекомендации

  • Использование try...catch и правильная настройка тайм-аутов — важнейшие инструменты для обработки ошибок в WebdriverIO.
  • Важно контролировать ошибки при взаимодействии с элементами, так как это наиболее частая причина сбоев в тестах.
  • Логирование ошибок и использование ассертов помогает ускорить диагностику и улучшить читаемость тестов.
  • WebdriverIO предоставляет множество встроенных возможностей для работы с ошибками в асинхронном коде, включая обработку сетевых запросов и взаимодействие с DOM.