Как избавиться от нестабильных тестов

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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

Автоматизированные тесты в Wrike

Wrike как продукт достаточно сложен в тестировании. Поддерживать высокое качество продукта нам помогают:

  • Более 50 000 тестов на Selenium.

  • Свыше 20 000 API-тестов.

  • Ежедневные деплои с полностью автоматизированным регрессионным тестированием.

  • От 400 000 до 500 000 уникальных результатов тестов каждый день (включая ветки разработчиков).

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

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

Мы столкнулись с двумя проблемами, которые связаны с нестабильностью тестов.

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

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

Как мы боремся с нестабильными тестами

Как же мы боремся с нестабильными тестами? В первую очередь мы перезапускаем (retry) тесты внутри билда. И это довольно распространенный подход.

Параллельный перезапуск двух упавших тестов
Параллельный перезапуск двух упавших тестов

На скриншоте выше вы можете увидеть таймлайн Allure: при первой попытке два теста не прошли. Затем мы попытались повторить эти тесты, запустив их еще раз. Обычно это делается последовательно, но мы захотели ускориться и улучшить нашу систему, поэтому перезапустили тесты параллельно (подробнее об этом мы рассказывали в этой статье). И, как вы можете видеть, это довольно эффективно. Как только тесты прошли, мы выбираем зеленые результаты и продолжаем.

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

Два упавших теста перезапускаются в другой сборке
Два упавших теста перезапускаются в другой сборке

Еще одна мера — это карантин тестов или отключение (mute). В большинстве систем CI (TeamCity или подобные), есть встроенная функция под названием «Mute», которая может скрывать результаты неудачных тестов. Такие тесты все равно должны быть запущены в сборке, но тестировщики даже не смотрят на их результаты, потомы что тесты отключены. В нашем случае они перезапускались бы несколько раз, пытаясь снова пройти. 

Но мы не хотели этого, поэтому разработали систему карантина тестов. 

Отключенные тесты не перезапускаются
Отключенные тесты не перезапускаются

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

Простая схема
Простая схема

И, наконец, мы анализируем результаты тестов, чтобы найти нестабильные. В основном это работает так. У нас есть некоторое количество тестов (N), затем мы применяем к ним некую «магию», чтобы получить список нестабильных тестов, и эти тесты отключаются. Таким образом, мы вообще не запускаем их в сборках, пока не исправим.

Выдуманная история, основанная на реальных событиях

Теперь я расскажу вам небольшую выдуманную историю, основанную на реальных событиях. Главный герой истории — Федор, он решает инженерную проблему и носит федору, поэтому выглядит как достойный доверия инженер.

Федор старается изо всех сил
Федор старается изо всех сил

Коллеги злятся на Федора из-за нестабильности тестов. Он пытается выяснить, что именно значит фраза: «Наши тесты нестабильны». И это на самом деле очень хороший и глубокий вопрос, на который Федор получил такой ответ:

Нестабильный тест в естественной среде обитания
Нестабильный тест в естественной среде обитания

У теста действительно низкий процент успешности (success rate) — 28%. Федор смотрит на статистику и видит, что неудачные результаты еще и замедляют тест. Федор пытается нажать на кнопку Help, но это не помогает. Тогда он предлагает разработать сервис для отключения тестов с процентом успешности менее 80%. Он просто выдумал эти 80% без особой причины, этот процент просто показался разумным.

Но скоро раздраженные коллеги снова обращаются к Федору с вопросом: «Почему мой новый тест был почти сразу же отключен?». Как же так получилось? 

Сразу в бан
Сразу в бан

Коллеги написали новый тест. У него было всего три результата. Один из них случайно не прошел, и тест сразу же был отправлен в бан. Точнее, в mute.

Первое улучшение в сервисе — собирать данные минимум за 2 недели и анализировать только те тесты, у которых в статистике есть как минимум 10 результатов. Почему 10? Потому что обычно 2 недели — это по 5 рабочих дней в каждой. Если мы запускаем тест только один раз в день, у него есть 10 результатов.

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

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

Ситуация связана с инфраструктурными проблемами. Опыт показывает, что инженеры, которые работают с автоматизированными тестами, часто сталкиваются с проблемами инфраструктуры. В некоторых случаях это могут быть периодические проблемы или короткие перебои. В других — ошибки в конфигурации или случайные сетевые задержки. Нет ни одного сервиса с 100% доступностью, но этого достаточно, чтобы тесты не проходили, и нам приходится запускать их снова, чтобы увидеть их зеленые результаты.

Но что на самом деле произошло в том случае? Ошибки в конфигурации. Если сборка с тестами запущена с опечаткой в конфигурации, то такая сборка, как правило, не проходит. Вот что на самом деле произошло. Один из коллег Федора проснулся очень сонным и допустил опечатку в параметре своей сборки. Сборка не прошла, он перезапустил ее еще раз, надеясь, что со второго раза сработает. Но она снова не прошла с теми же результатами, затем снова и снова. Когда коллега нашел опечатку и исправил ее, процент успешности теста был ужасно испорчен.

Так Федор внес еще одну серию улучшений. Теперь сервис фильтрует инфраструктурные проблемы. Федор собрал более 100 шаблонов ошибок, которые выглядели примерно так: 502 Bad Gateway, Chrome is not reachable, Test filter is not correct и т.д. Теперь сервис может анализировать, с какой ошибкой или стек трейсом упал тест и фильтровать инфраструктурные проблемы. Это не работает на 100%, потому что таким образом невозможно обнаружить каждую инфраструктурную проблем, а только те проблемы, которые обнаруживаются по сообщению об ошибке. Но это действительно полезная вещь, и она значительно улучшила сервис.

Но раздраженные коллеги снова обратились к Федору: «Мой тест не виноват. Почему он отключен?». Давайте посмотрим, что произошло. 

Вот как выглядит статистика теста:

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

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

Дальше все шло хорошо, но только до тех пор, пока не пошло плохо. Еще одна проблема, с которой коллеги обратились к Федору: «Мы только что исправили тест, но затем он снова был отключен. Что произошло?».

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

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

«Мои тесты справедливо падают, но они отключены», — еще одно раздраженное сообщение Федору. И это действительно интересный момент, потому что тесты на самом деле предназначены для валидных падений, чтобы показать ошибки. Это именно то, что они должны делать. В этом случае коллеги тестировали большую новую фичу. Поэтому многие падения тестов на самом деле ожидались, некоторые истории еще не полностью завершены, и их тесты еще не готовы работать с новым поведением продукта. Это совершенно нормально.

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

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

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

Мы делаем это с помощью метрик. Метрики показывают множество вещей, вот некоторые примеры.

Процент отключенных тестов (слева) помогает проследить, чтобы мы не отключили ненароком все тесты в проекте. Изменение отключенных тестов (справа) показывает графики с отключенными и включенными тестами в день. Это наглядно показывает, что система отключения и включения работает и позволяет детализировать картину.

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

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

Кажется, баланс найден. Но раздраженные коллеги снова приходят к Федору: «Мои тесты справедливо падают (опять)!». Даже если нет большой фичи в разработке, тесты все равно предназначены для справедливого провала и поиска ошибок. Это все еще чистая правда. 

В итоге Федор придумал лучшее улучшение сервиса на данный момент. Какие упавшие тесты на самом деле нестабильны? Он подумал об этом и вспомнил один из способов сделать тесты более стабильными — повторные попытки тестов внутри сборки. Это механизм, который помогает стабилизировать сборки, но скрывает результаты неудачных тестов, перекрывая их новыми прошедшими и маскируя нестабильные тесты. Так что, если тест упал в сборке, а затем внезапно прошел, он, скорее всего, нестабильный. Но не только это открытие сделал Федор. Если тест не прошел в сборке, но причина неудачи отличается от раза к разу, это также маркер нестабильного теста. Звучит очевидно, но это не то, что первым делом приходит в голову, когда речь идет о нестабильных тестах, верно?

Маркеры нестабильности тестов
Маркеры нестабильности тестов

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

Федор пытается объяснить коллегам, почему их тесты замьютились
Федор пытается объяснить коллегам, почему их тесты замьютились

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

Пример описания нестабильного теста
Пример описания нестабильного теста

После этого система заработала весьма хорошо. Тесты запускаются, статистика собирается и анализируется автоматически. Нестабильные тесты отключаются, задачи создаются и назначаются ответственным людям с понятным описанием и уведомлением. Если тест исправлен каким-то побочным эффектом, он может быть автоматически включен после некоторой серии успешных запусков. Также теперь можно обнаруживать нестабильные баги, которые раньше скрывались повторными попытками (retry). Это стало возможным, потому что теперь тестировщикам приходится углубляться в причины нестабильности тестов. 

Что я хочу сказать в заключение: success rate тестов не всегда то, чем кажется. Для поиска нестабильных тестов retry rate — гораздо лучший выбор. Но процент успешности — это метрика, которую на самом деле видят пользователи автоматизированных тестов, поэтому важно не забывать обращать внимание и на нее.

Давайте теперь подведем итог алгоритму и посмотрим все советы из истории Федора по обнаружению нестабильных тестов:

  • Используйте только одобренные конфигурации (CI-сборки) для лучшей статистики тестов.

  • Собирайте статистику минимум за 10-15 дней.

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

  • Фильтруйте инфраструктурные проблемы по сообщениям об ошибках или стек трейсам.

  • Сбрасывайте статистику теста после того, как его исправили и выключили.

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

  • Сортируйте тесты по числу фактических сбоев (чтобы учесть процент успешности).

  • Отключайте N тестов с наивысшим коэффициентом повторного использования и фактическим коэффициентом сбоев.

P. S.

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

Сервис написан на Java с использованием Spring Boot для внедрения зависимостей и запуска по расписанию, для простоты это может быть даже jar, запущенный с помощью cron. Статистика тестов берется из Allure TestOps через API. 

Затем статистика трансформируется и хранится в базе данных Postgres. Для этого есть две причины. Во-первых, запросы по API создали бы дополнительную нагрузку на Allure при сборе данных для каждого конкретного теста или билда. Во-вторых, получение данных из индексированной базы данных, которая объединена с некоторыми дополнительными данными, отсутствующими в Allure (например, история отключения тестов), происходит намного быстрее. Затем нестабильные тесты отключаются, создаются задачи и назначаются ответственным людям. Для этого код должен быть правильно размечен, но это уже совсем другая история.

Источник: https://habr.com/ru/companies/wrike/articles/788396/


Интересные статьи

Интересные статьи

Всем привет.Сегодня я хотел бы поговорить немного про тестирование в 1С.Так уж сложилось, что мне посчастливилось довольно основательно погрузиться в эту тему и я хотел бы немного поделиться с вами св...
Недавно английский эксперт по SEO Уилл Кричлоу опубликовал обзор опций по применению Chat GPT под заглавием «8 миллионов тестовых идей для Chat GPT».
Привет, Хабр. Меня зовут Дмитрий Гусаков. Я тимлид команды QA в компании Arenadata. Наша команда занимается тестированием компонентов Arenadata Enterprise Data Platform, в том числе тестированием орке...
«… я раньше думал, что могу писать программы без ошибок» Это вторая часть интервью. Первую часть можно прочитать здесь. Адам: Привет и добро пожаловать в CoRecursive. Я Адам Гордо...
Разрабатывая API, наверняка не раз появлялись сложности с документацией: то её нет, то она не отображает поведение, описанное в коде. С точки зрения разработчика, написание документации (од...