Flaky тесты — это тесты, которые непредсказуемо проходят или не проходят при каждом запуске, даже если код не изменялся. Основные причины flaky тестов:
Один из самых частых источников flaky тестов — это проблемы синхронизации, когда элементы на странице еще не загружены, а тест уже пытается с ними взаимодействовать. В WebDriverIO можно использовать явные ожидания для того, чтобы тест ждал появления элемента на странице.
Пример:
const button = await $('#submit-button');
await button.waitForExist({ timeout: 5000 }); // Ожидание до 5 секунд, пока элемент не появится
await button.click();
waitForExist гарантирует, что WebDriverIO не продолжит
выполнение теста до тех пор, пока элемент не будет найден, что
предотвращает возможные ошибки из-за раннего обращения к элементу.
Современные веб-приложения часто взаимодействуют с сервером, выполняя асинхронные запросы и обновляя DOM. Это может вызвать flaky тесты, если тест не учитывает завершение этих операций. Для таких случаев следует использовать методы, которые обеспечивают ожидание завершения запросов или анимаций.
Пример:
await browser.waitUntil(async () => {
const value = await $('#status').getText();
return value === 'loaded';
}, {
timeout: 10000, // Ожидание до 10 секунд
timeoutMsg: 'Status did not load in time'
});
В этом примере waitUntil будет проверять значение на
элементе до тех пор, пока оно не станет равным 'loaded',
или пока не истечет тайм-аут.
Для того чтобы избежать flaky тестов, необходимо обеспечить независимость каждого теста от других. Это можно достичь путем изоляции состояния между тестами.
В WebDriverIO можно использовать хуки для подготовки и очистки тестового окружения перед и после выполнения каждого теста:
beforeEach(async () => {
await browser.url('https://example.com');
// Другие действия по подготовке окружения
});
afterEach(async () => {
await browser.reloadSession();
// Очистка данных после теста
});
Это позволяет избежать зависимости тестов друг от друга и гарантировать, что каждый тест будет выполнен в чистом состоянии.
Использование нестабильных локаторов может стать причиной flaky тестов. Например, локаторы, зависящие от динамических данных, могут изменяться с каждым запуском тестов.
Рекомендуется использовать более стабильные локаторы, такие как:
Пример использования стабильного локатора:
const submitButton = await $('#submit-button'); // Уникальный ID
await submitButton.click();
Когда тесты запускаются параллельно, важно избегать конфликтов между
ними. Для этого необходимо изолировать данные и состояние каждого теста.
В WebDriverIO можно использовать parallel для параллельного
выполнения тестов, а также гарантировать, что они не будут
взаимодействовать друг с другом.
Пример:
exports.config = {
runner: 'local',
specs: ['./tests/**/*.js'],
maxInstances: 5, // Параллельный запуск 5 тестов
capabilities: [{
browserName: 'chrome',
'goog:chromeOptions': {
args: ['--headless']
}
}],
// Другие настройки конфигурации
};
В таком случае WebDriverIO автоматически управляет количеством параллельных инстансов, а изоляция данных тестов обеспечивается настройками конфигурации.
Иногда flaky тесты возникают из-за зависимостей от сторонних сервисов или нестабильных API. Для предотвращения таких проблем можно использовать моки, чтобы заменять реальное взаимодействие с сервером на заранее подготовленные данные.
Пример использования моков:
browser.url('https://example.com');
// Мокаем запрос к API
browser.mockRequest('GET', '/api/data', (req) => {
req.respond(200, { data: 'mockedData' });
});
// Выполняем тест, который теперь использует мокированные данные
await $('#getDataButton').click();
const result = await $('#result').getText();
expect(result).toBe('mockedData');
Моки позволяют контролировать состояние данных в тестах и предотвращают зависимость от внешних источников.
Не менее важным является проверка состояния страницы перед выполнением действия. Это поможет избежать ситуаций, когда тест пытается взаимодействовать с элементом, который не готов к использованию.
Пример проверки состояния:
const isPageReady = await browser.execute(() => {
return document.readyState === 'complete';
});
expect(isPageReady).toBe(true);
В этом примере перед выполнением теста проверяется, что страница полностью загружена и готова к взаимодействию.
При тестировании динамичных интерфейсов важно избегать зависимостей
от мелких изменений в верстке. Лучше всего использовать логические и
семантические элементы интерфейса (например, атрибуты
data-*, заголовки или другие уникальные и понятные
элементы), чем элементы, которые могут изменяться в ходе разработки.
Пример:
const product = await $('[data-test="product-123"]');
await product.click();
Использование атрибутов data-* позволяет обеспечивать
стабильность локаторов при изменениях в структуре страницы.
Предотвращение flaky тестов требует внимательности и правильного подхода к синхронизации, изоляции тестов, стабильности локаторов и обработке асинхронных операций. Применение лучших практик, таких как явные ожидания, использование мока, правильное управление состоянием и параллельным выполнением тестов, позволяет существенно улучшить стабильность тестового набора и сделать его более надежным.