Автоматизируй это! Как мы улучшали интеграционное тестирование

    В давние времена у нас было всего несколько сервисов, и выложить за сутки обновление более чем одного из них на production — было большой удачей работой. Потом мир ускорился, система усложнилась, а мы трансформировались в организацию с микросервисной архитектурой. Теперь у нас около сотни сервисов, и вместе с ростом их числа увеличивается и частота релизов — их более 250 в неделю.

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



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

    Про изменения в релизном цикле и развитие механизма выкладки рассказывали со стороны ops и dev в одной из прошлых статей. Я же расскажу про историю изменения процессов тестирования в ходе этой трансформации. 

    Сейчас у нас около 30 команд разработки. В команду обычно входят руководитель продукта, менеджер проекта, фронтенд- и бэкенд-разработчики и тестировщики. Их объединяет работа над задачами по конкретному продукту. За сервис, как правило, отвечает команда, которая чаще всего вносит в него изменения. 

    Приемочное end-to-end тестирование


    Ещё не так давно при релизе каждого компонента прогонялись только unit- и компонентные тесты, а после этого на полноценной тестовой среде выполнялись лишь несколько самых важных end-to-end сценариев перед выкладкой сервиса в продакшен. Вместе с ростом числа компонентов стало экспоненциально увеличиваться и число связей между ними. Зачастую — совсем нетривиальных связей. Вспоминаю, как недоступность сервиса выдачи маркетинговых данных поломала регистрацию пользователей напрочь (разумеется, на короткое время).

    Такой подход к проверке изменений стал давать сбои все чаще и чаще — потребовались покрытие автотестами всех критичных бизнес-сценариев и их прогон на полноценном тестовом окружении с готовой к релизу версией компонента.

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

    Мы постарались по максимуму использовать инструменты, уже применяемые для проведения компонента по релизному циклу и запуска задач: Jira и Jenkins соответственно.

    Цикл приемочного тестирования


    Для проведения приемочного тестирования определили такой цикл: 
    1. мониторинг поступающих задач на приемочное тестирование релиза,
    2. запуск Jenkins job для установки релизной сборки на тестовую среду,
    3. проверка, что сервис поднялся,
    4. запуск Jenkins job с интеграционными тестами,
    5. анализ результатов прогона,
    6. повторный прогон тестов (при необходимости),
    7. обновление статуса задачи — пройдена или сломана, с указанием причины в комментарии.

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

    Бот-мониторщик


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

    Данные для формирования оповещений поступают в виде пуш-уведомлений от Jira. После запуска бота мы перестали обновлять страницу дашборда с приемочными тасками, и ширина улыбки автоматизатора чуть увеличилась.

    Pinger


    Мы решили упростить проверку того, что во время развертывания в тестовой среде не случилось ошибок сборки или установки и что поднялась именно нужная версия компонента, а не какая-то другая. Свои версию и статус компонент отдает по протоколу HTTP. И проверка того, что сервис возвращает корректную версию, оказалась бы простой и понятной, если бы разные компоненты не были написаны на разных языках — какие-то на Node.js, какие-то на C#. Вдобавок наши самые массовые сервисы на Java тоже отдавали версию в разном формате. 

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

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



    Locker


    Пришло время более сложных задач — автоматического обновления компонентов и запуска тестов. На тот момент у нашей команды уже было 3 тестовых стенда в OpenStack для проведения приемочных испытаний, и сначала нужно было решить проблему управления ресурсами тестовых стендов: будет неприятно, если при прогоне тестов на систему «покатится» обновление следующего релиза. Ещё бывает, что тестовый стенд отлаживают, и тогда не стоит использовать его для приёмки.
    Хотелось иметь возможность посмотреть статус занятости и при необходимости вручную заблокировать стенд на время разбора упавших тестов или до завершения других работ. 

    Для всего этого появился сервис Locker. Он долговременно хранит статус тестового стенда («занят»/«свободен»), позволяет задать комментарий к «занятости», чтобы было понятно, что сейчас мы отлаживаемся, пересоздаем экземпляр тестовой среды или прогоняем тесты очередного релиза. Ещё мы стали блокировать стенды на ночь — на них администраторы проводят работы по расписанию, вроде бэкапов и синхронизации баз. 

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

    Дежурство


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

    C ростом количества релизов появилась роль второго дежурного, который подключается к основному, если возникают «завалы» или в очереди есть критичные релизы. Для предоставления информации о ходе тестирования релизов мы создали страницу с числом задач в состояниях «открыт»/«выполняется»/«ожидает реакции дежурного», статусом блокировки тестовых стендов и недоступными на стендах компонентами:


    Работа дежурного требует концентрации, поэтому у него есть плюшка — в день дежурства он может выбрать место для обеда всей команды недалеко от офиса. Особенно весело выглядят подкупы дежурного в стиле: «давай я помогу разобрать задачи, а сегодня пойдем в моё любимое место» =)

    Reporter


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

    Кроме того, у нас появились новые особенности работы.

    1. Возникла категория тестов, которые падают с большей или меньшей периодичностью за счет проблем с тестовыми стендами. Падения могут возникать из-за возросшего времени ответа одного из сервисов или долгой загрузки ресурсов в браузере. Выключать тесты не хочется, разумные средства повышения их надежности исчерпаны. 
    2. У нас появился второй, экспериментальный проект с автотестами, и всплыла необходимость разбирать прогоны сразу двух проектов, просматривая отчеты Allure.
    3. Прогон тестов может занимать до 20 минут, а приступать к анализу результатов хочется сразу после начала первых падений. Особенно если задача критичная и члены ответственной за релиз команды стоят у тебя за спиной, приставив нож к горлу с жалобными глазами.

    Так появился сервис Reporter. В него мы пушим результаты прогона тестов в реальном времени в процессе тестирования. В сервисе организована база известных проблем или багов, которые слинкованы с конкретным тестом. Также была добавлена публикация на wiki-портале компании сводного отчета по результатам прогона из репортера. Это удобно для менеджеров, которые не хотят погружаться в технические детали, которыми изобилует интерфейс Reporter или Allure.

    При падении теста можно посмотреть в Reporter список связанных с ним багов или задач на исправление. Такая информация сокращает время разбора и облегчает обмен знаниями о проблемах между членами нашей команды. Записи о выполненных задачах уходят в архив, но если нужно, можно их «подсмотреть» в отдельном списке. Чтобы не грузить внутренние сервисы в рабочие часы, опрашиваем Jira по ночам и записи для issues с финальным статусом архивируем.

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

    Autorun


    Далее мы перешли к автоматизации запуска тестов, когда в issue tracker приходит задача на приемочное тестирование релиза. Для этой цели был написан сервис Autorun, который проверяет, есть ли в Jira новые задачи на приёмку, и если да, то определяет имя компонента и его версию на основании контента задачи. 

    Для задачи выполняются несколько этапов:

    1. берем lock одного из свободных тестовых стендов в сервисе Locker,
    2. запускаем инсталляцию нужного компонента в Jenkins, ждем поднятия компонента с требуемой версией,
    3. запускаем тесты,
    4. ждем завершения прогона тестов, в процессе их выполнения все результаты пушатся в Reporter,
    5. запрашиваем у Reporter количество упавших тестов, исключая упавшие из-за известных проблем,
    6. если упало 0 — переводим задачу на приёмочное тестирование в «Готово» и завершаем работу с ней. Всё готово =) 
    7. если есть «красные» тесты — переводим задачу в «Ожидание» и идем в Reporter для их разбора. 

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



    Всё это позволяет автоматически передавать дальше по конвейеру развертывания релизы, по которым 100 процентов тестов — зеленые. Но как быть с нестабильностью, вызванной не проблемами в компоненте, а «природными» особенностями UI-тестов или подросшими сетевыми задержками в тестовом стенде?

    Для этого у нас реализован механизм повторных запусков (retry), которым многие пользуются, но мало кто в этом сознается. Ретраи организованы как последовательный запуск тестов в Jenkins Pipeline. 

    После прогона мы запрашиваем список упавших тестов у Reporter из Jenkins — и перезапускаем только провалившиеся. Кроме того — снижаем число потоков при запуске. Если число упавших тестов не уменьшилось по сравнению с предыдущим прогоном — сразу завершаем Job. В нашем случае такой подход к перезапуску позволяет повысить успешность приемочного тестирования примерно в 2 раза.

    Quick-block


    Получившаяся система приемочного тестирования позволила нам проводить больше 60% релизов без участия человека. Но что делать с оставшимися? При необходимости дежурный создает багрепорт на тестируемый компонент или задачу на исправление тестов в команду разработки. Иногда — оформляет баг конфигурации тестового стенда в отдел эксплуатации.

    Задачи на исправление тестов часто блокируют корректное прохождение автоматического тестирования, так как неактуальные тесты будут всегда «красными». За написание новых тестов и актуализацию существующих отвечают тестировщики из команд разработки — внося изменения через pull request-ы в проект с автотестами. Эти правки проходят обязательное ревью, которое требует некоторого времени от ревьювера и от автора, и хочется временно заблокировать неактуальные тесты до перевода задачи на их исправление в финальный статус. 

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

    Поэтому мы перенесли список блокирующих тесты задач в новый сервис с web-страницей — Quick-block. Так члены команды, ответственной за компонент, могут оперативно заблокировать тест. Перед прогоном мы ходим в этот сервис и получаем список тестов из карантина, которые переводим в статус skipped.

    Итоги


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

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

    Повышение надежности тестов не только увеличит доверие к ним, но и ускорит тестирование релизов за счет отсутствия перезапусков упавших сценариев.
    Яндекс.Деньги
    102,50
    Как мы делаем Деньги
    Поделиться публикацией

    Комментарии 17

      +1
      5. запрашиваем у Reporter количество упавших тестов, исключая упавшие из-за известных проблем
      Вопрос 1. Если известные проблемы падения теста — это существующие в bugtracker issues, которые ещё не пофиксили, почему бы автоматически не исключать такие тесты из прогона, сокращая его время?
      После прогона мы запрашиваем список упавших тестов у Reporter из Jenkins — и перезапускаем только провалившиеся.
      Вопрос 2. Если какие-то провалившиеся пройдут, списывается ли это на наводки стенда? а что, если там периодический баг или, например, причина его проявления зависит от предыдущего, успешного теста, который Вы исключили?
        +1
        Спасибо за вопросы!

        1. Исключаем с помощью сервиса quick-block. После решения задачи тест автоматом начнет запускаться, так как проверяем актуальность задачи в jira перед каждым запуском приёмки.
        2. На практике, часто падающие тесты анализируются после сбора статистики, и в причинах их падения мы разбираемся. Исключить пропуск плавающих багов нельзя, но при нашем числе релизов лучше принимать такой риск, чем тормозить релизный цикл за счет ручного разбора упавших flaky-тестов.
          0
          при нашем числе релизов лучше принимать такой риск, чем тормозить релизный цикл
          Рекомендую минимизировать любые риски, тем более в Яндекс.Деньгах, иначе, чем больше релизов гоните при таких допущениях, тем выше вероятность недоразумений.
            0
            Из Вашего ответа на Вопрос 1 и по тексту статьи, Вы, в том числе с помощью Pinger, практикуете проверки интеграции на разных версиях компонент. При этом вручную, используя Quick-block, исключаете из прогона тесты, в зависимости от наличия в соответствующей проверяемой функциональности, найденных/непофикшенных на тот момент багов (issues) прикладной разработки в соответствующих версиях интегрируемых компонент.
            Вероятно, и тесты автоматизации у Вас версионируются, связаны с соответствующими версиями компонент. У соответствующих версий автоматизированных проверок в определённых тестах есть известные ошибки автоматизации и эти тесты также должны исключаться из прогона.

            Вопрос 3. Я правильно понимаю, что это происходит типа практически вручную:
            — выбрать версию тестируемого компонента, получить для неё список известных ошибок;
            — выбрать версию тестов автоматизации в прогоне, получить список ошибочных тестов;
            — по ним агрегировать список заведомо сбойных тестов и отключить их в прогоне?
          –2
          По канону там девушка должна с ноутом сидеть.
            0
            Если дежурный понимает, что не справляется с потоком задач, он может попросить помощи у команды. В это время остальные члены команды занимаются задачами, не связанными с релизами.
            Опираясь на этот фрагмент из статьи, там скорее запечатлено какое-то одно из двух этих утверждений.
            +1
            Расскажите по поводу ретраев:
            Если тест параметризован и один из них упал — перезапускается ли вся параметризация?
              0
              При перезапуске параметризованных тестов «из коробки» заново прогоняются все комбинации. У нас немного параметризованных сценариев в приемке, они чаще используются в других типах автотестов.
              +1
              Ничего не понятно. Добавляете фичу, отключаете падающие от изменений тесты, пока их не актуализируют, пишите новые тесты на фичу, а тем временем фича релизится без тестирования на прод?
                0

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

                0

                Яндекс.Деньги? Скажите, вы когда-нибудь дадите возможность оплачивать штрафы с карты, прикреплённой к ящикам Почты для домена?!

                0

                Спасибо за увлекательную статью! Интересны подробности :)


                1. Сколько у вас приемочных тестов? Какова стабильность?
                2. Какие классы приемочных сценариев вы покрыли тестами, а какие — нет? Интересуют не сценарии продукта, а категории типа "совместимость нового компонента с данными старой версии" или "сценарии отката релизас
                3. Если команды разработки сами пишут приемочные тесты, то почему они не разбирают их падения и почему гоняют тесты только перед релизом? Могли бы делать это раньше, например до своего ручного тестирования :)
                4. Я правильно понял, что при деплое нового компонента на стенд, версии других на стенде сбрасываются до релизных? Если нет, то на какой версии всего продукта гоняются тесты?
                5. Как у вас релизятся фичи, в которых надо согласованно обновить несколько компонентов от разных команд?
                  0
                  1. Какие требования вы предъявляете к пулл-реквестам в проект с тестами?
                    0

                    Code review осуществляем в bitbucket, в соответствии с successfull git branching model.
                    Изменения должны быть оформлены в соответствии с нашим Coding Convention. Для merge обязателен аппрув от члена команды интеграционного тестирования.


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

                    0
                    Тоже интересно узнать ответы на 1, 3 и 4 вопросы
                      0
                      1. Приемочных тестов на данный момент около 1500, они покрывают end-2-end сценарии разной степени сложности: иногда в сценарии участвуют 2 компонента, иногда 22. Компонентные тесты на сервисы, unit тесты — запускаются отдельно. Стабильность зависит от множества факторов, например, от того, задействован ли web-браузер в тесте, сколько компонентов затрагивает сценарий, как нагружены тестовая среда и CI-инструментарий. Перепрогоны в jenkins pipeline позволили снизить частоту откровенно случайных падений почти до 0.


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


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


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


                      5. Здесь без вмешательства человека не обходится. О таких коллизиях нас предупреждают команды, и мы уже самостоятельно разрешаем все зависимости.


                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое