Как исправить нестабильные сквозные тесты с помощью Playwright и Reflow

Как исправить нестабильные сквозные тесты с помощью Playwright и Reflow

15 апреля 2022 г.

Сквозное тестирование — это метод обеспечения качества, который включает отработку и проверку рабочего процесса приложения от начала до конца.


Этот метод направлен на воспроизведение реальных пользовательских сценариев и проверку ожиданий по выходным данным приложения.


К сожалению, сквозное тестирование имеет проблемы, которые затрудняют поддержку тестов:


  1. В процессе тестирования обычно предполагается, что система находится в согласованном начальном состоянии. Для этого может потребоваться заполнение или удаление тестовых данных до/после выполнения теста.

  1. Когда вносятся изменения в приложение, может потребоваться внести изменения в тестовую последовательность для обработки обновления приложения.

  1. Даже когда ничего не меняется, иногда тесты все равно не проходят. Такие тесты обозначаются как flaky.

За последние 18 месяцев я запускал reflow.io: SaaS-инструмент, который позволяет нетехническим специалистам по обеспечению качества внедрять и поддерживать сквозные тесты с использованием Интерфейс записи кода. Я накопил набор стратегий для исправления нестабильных тестов и автоматического исправления последовательности тестов при наличии изменений в приложении.


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


Почему это важно?


Время — ценный ресурс для всех продуктовых команд. Его нужно беречь и расходовать с умом. Ненадежные тесты требуют времени.


  • Каждый раз, когда тест дает сбои, как правило, кто-то выясняет, является ли сбой истинным сбоем или ложноположительным. Затраты времени на это могут стать очень значительными.

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

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

Эмоциональный ущерб


  • Само существование автоматизированных тестов подразумевает, что кто-то достаточно позаботился о том, чтобы автоматизировать часть ручного труда.

  • Когда это ручное усилие возвращается через тестовую пластину, это сказывается на эмоциях.

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

Доверие к автоматизации


  • Если тесты ненадежны, им нельзя доверять.

  • Если тестам не доверяют, они постепенно устареют и в конечном итоге будут удалены.

  • Если команда продукта не заменит эти тесты, кодовая база потеряет тестовое покрытие и может стать менее надежным.

  • Если кодовая база менее надежна, пострадает производительность.

Смягчение


Стратегия 1: Общая стабильность перед действием


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


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


В драматурге этот метод стабилизации доступен в API waitForLoadState.


```тс


await page.waitForLoadState('domcontentloaded', {время ожидания: 15000});


await page.waitForLoadState('загрузка', {время ожидания: 30000});


await page.waitForLoadState('networkidle', {время ожидания: 5000});


Эти 3 события загрузки:


  1. domcontentloaded – дождитесь запуска события DOMContentLoaded. Это срабатывает, когда исходный документ страницы загружается и анализируется. Для SPA таблицы стилей, изображения и javascript, как правило, не загружаются при срабатывании этого события. Следовательно, обычно предпочтительнее полагаться на событие load.

  1. load - дождитесь запуска события загрузки. Это событие срабатывает, когда вся разметка, таблица стилей, javascript и все статические ресурсы, такие как изображения и аудио, загружены. Для многих SPA, которые динамически загружают данные после того, как страница отображает исходный документ, события load может быть недостаточно.

  1. networkidle - дождаться отсутствия сетевых подключений не менее 500 мс. Это полезное событие для подключения к SPA, которые загружают данные после их первоначального рендеринга. Однако для взаимодействия со страницей может быть либо слишком рано, либо слишком поздно.

В reflow есть необязательное 4-е событие: screenshotstable. Это событие запускается, когда страница:


  1. Не изменился в течение 5 с.

  1. Выглядит так же, как страница при последнем успешном тестировании.

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


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


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


Стратегия 2: интеллектуальное ожидание


Если действие ожидается на данной странице, мы можем дождаться завершения перехода на эту страницу с помощью page.waitForNavigation. Это может быть важно, так как во время последовательности навигации может срабатывать несколько событий загрузки: следовательно, page.waitForLoadState не всегда будет достаточно.


Если действие влияет на конкретный элемент, мы можем выполнить дальнейшие действия для конкретных действий. Например:


  1. Подождите, пока элемент будет прикреплен к DOM.

  1. Подождите, пока элемент станет видимым.

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

  1. Подождите, пока элемент сможет получать события

  1. Если элемент кликабельный, подождите, пока он не станет включенным.

  1. Если элемент является элементом ввода текста, подождите, пока элемент станет редактируемым.

В драматурге эти [утверждения стабильности перед действием автоматически применяются в зависимости от типа взаимодействия] (https://playwright.dev/docs/actionability).


При оплавлении стабильность дополнительно повышается за счет:


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

  1. Примените определенный тайм-аут к элементу в зависимости от того, сколько времени потребовалось для применения последнего обновления атрибута DOM во время последнего успешного запуска. Например. если кнопка стала зеленой из-за изменения атрибута class через 7 секунд, подождите не менее 7 секунд, пока элемент получит изменения атрибута DOM, перед дальнейшими тайм-аутами.

Стратегия 3: Выбирайте хорошие селекторы


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


Умная команда разработчиков будет бороться с этим с помощью строго согласованного атрибута элемента, такого как data-test-id. Например.


```tsx


<кнопка


data-test-id={тест-действия-${testId}}


Чтобы нажать такую ​​кнопку, мы можем использовать атрибут «data-test-id», чтобы найти элемент.


```тс


await page.click([data-test-id="test-actions-${testId}"]);


Другими хорошими вариантами для сценариев, в которых добавление определенного селектора элемента нежелательно, являются:


| Селектор | Описание |


| заполнитель="..." | Заполнители часто уникальны для элемента |


| [aria-label="..."] | Атрибут, используемый вспомогательными технологиями для идентификации элемента (например, для программы чтения с экрана) |


| img[alt="..."] | Атрибут, используемый для обозначения альтернативного текста для изображения, если изображение не может быть отображено |


| роль="..." | Атрибут роли используется для добавления семантического значения элементу вспомогательных технологий (например, программ чтения с экрана) |


| вход[тип="..." | Ввод часто имеет уникальный/неизменный тип, который ссылается на него для кратких форм |


| имя_узла | Если тип узла элемента используется только один раз, nodeName (например, [a ; input ; button]) часто является хорошим выбором |


| #... | Элементам часто присваиваются уникальные атрибуты id, чтобы облегчить их поиск с помощью javascript. |


Reflow автоматически подберет такие селекторы во время тестовой записи и оценит их по уникальности и типу. Это также будет:


  1. Объедините родительские селекторы с дочерними, чтобы уменьшить неоднозначность. Например. [data-test-id="foo"] >> [data-test-id="bar"].

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

  1. Сравните все элементы страницы, полученные из частичных совпадений селектора, со снимком экрана самого последнего успешного элемента, чтобы включить автоматическое восстановление локатора, когда он не может быть найден или является неоднозначным. Мы называем это «визуальным селектором».

Стратегия 4: контрольные точки «подождать»


Эта стратегия основана на написании логики для конкретного приложения, которая должна быть построена для обеспечения того, чтобы приложение находилось в определенном состоянии.


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


```тс


ожидание page.waitForSelector('[aria-label="расчет"] >> текст=29,76')


В reflow такое утверждение может быть сделано с любым тайм-аутом. Например, в большинстве наборов внутренних тестов reflow (работает сам по себе) он начинается с:


  1. Делаем новый тест оплавления

  1. Переход к пользовательскому интерфейсу записи этого теста

  1. Ожидание, пока пользовательский интерфейс записи не будет выглядеть как предварительно записанный снимок экрана, до 5 минут.

Визуальное утверждение на шаге [3] устраняет нестабильность как времени запуска сервера, так и распространения DNS. Он не требует никакого кода и работает очень надежно. Когда начальный URL-адрес имеет какие-либо визуальные изменения, reflow не пройдет тест и предложит вариант [автоматического восстановления] (https://reflow.io/readme#auto-healing), чтобы быстро заменить его новым визуальным снимком.


Стратегия 5: Заполнение данных с нулевой зависимостью


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


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


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


```тс


await page.request.post(${event.variables.url}/reset/scenario/empty?token=${event.variables.secret});


Вывод: есть ли способ навсегда избавиться от всех ненадежных тестов?


Нет.


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


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


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



Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE