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

Меня зовут Василий Юдин, я инженер в Авито и техлид команды, которая разрабатывает инструменты для тестирования. Рассказываю, как мы с командой создали хранилище тестов с выгрузкой из кода и как оно помогло нам в работе. 

Почему мы решили делать свою TMS и сервис выгрузки тестов из кода

Раньше в Авито были отдельные команды разработки и QA. Тестировщики приходили на конкретные задачи и помогали разработчикам с тестами. 

Со временем компания выросла, мы начали применять Scrum, а QA-инженеры стали постоянными членами Dev-команды. При этом в начале спринта, когда работа над задачами только начиналась, тестировщикам было нечем заняться. Но к концу второй недели разработчики разом отдавали все фичи на тестирование, и QA оказывался в огне. Качество тестов падало, а человек постепенно выгорал. 

Проблему решило Agile-тестирование. Это практика, которая основана на одном из постулатов Scrum: за качество продукта отвечает вся команда. При таком подходе разработчики пишут unit-тесты в коде приложения. А тестировщик на этапе планирования подсвечивает кейсы, которые нужно протестировать, разрабатывает критерии приёмки и принимает тесты на код-ревью. 

Всё работало хорошо, но Авито продолжал расти. Знаний о качестве продукта становилось больше. QA-инженерам стало сложно держать в голове всю необходимую информацию о тестах. Качество продукта перестало быть прозрачным: приходилось собирать информацию отдельно от каждой команды. К тому же когда приходил новый сотрудник, его сложно было погрузить в работу, потому что данные о тестировании были у разных людей. Проблему мы решили тем, что внедрили TMS.  

Как выглядит тестовая модель и карточка тест-кейса в TMS Авито

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

Часть структуры тестовой модели в TMS Авито
Часть структуры тестовой модели в TMS Авито

В карточке тест-кейса прописывается всё, что нам нужно и важно отслеживать: название, приоритет, тип теста и принадлежность конкретной команде. Затем указывается ID ноды, к которой относится тест. Есть поля для описания тестового сценария и тестовой модели.

Карточка тест-кейса в TMS
Карточка тест-кейса в TMS

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

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

Как можно выгружать тесты из кода TMS

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

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

У выгрузки с прогоном тестов есть два значительных минуса:

  • чтобы вносить в отчёт новые данные, вероятно, придётся доработать тестовый фреймворк, например использовать хендлеры и ивенты; 

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

Выгрузку тестов без запуска кода мы разделили на три вида:

1. Рефлексия

Для многих языков разработки есть пакет для дебага приложения. Обычно в них есть библиотека reflection, которая позволяет достать нужные данные: прочитать аннотации к тест-кейсам, узнать дата-провайдера, выгрузить dataset. Например, такая библиотека есть для PHP, с ней удобно работать.

Но тут есть подводные камни. Допустим, чтобы получить dataset на JS, требуется запустить код проекта, в котором он используется. Для этого нужно подключить зависимости, поднять контейнер в Docker — работы становится больше, чем если заполнять тест-кейс в TMS вручную. 

2. Регулярные выражения

Довольно простой способ выгрузки. Достаточно сделать git clone проекта и написать несколько строк кода. Метод подходит для любого языка и тестового фреймворка, не требует зависимостей. 

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

3. Парсинг на ast-ноды

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

Такой же подход можно использовать для выгрузки тест-кейсов: разбить файл с кодом на ast-ноды, просмотреть дерево и выбрать те ветки, которые относятся к тест-кейсам. Затем перевести их, например, в JSON и перенести в TMS.

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

Как мы выгружаем тест-кейсы из кода с помощью регулярных выражений

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

«Боевая» регулярка Авито
«Боевая» регулярка Авито

При этом сами тест-кейсы нужно размечать достаточно подробно, чтобы собрать все нужные данные. У нас есть несколько обязательных полей: 

  • tag_id — ID ноды тестовой модели;

  • title — название теста;

  • testType — тип теста;

  • priority — приоритет. 

И ещё около 10 необязательных полей, таких как description, feature_id и precondition. 

Если размечать тест всеми этими полями, получится очень подробная и объёмная разметка. Это наглядно, но неудобно для разработчиков.

Первые четыре поля — дата-провайдеры, остальные — разметка тест-кейса для TMS
Первые четыре поля — дата-провайдеры, остальные — разметка тест-кейса для TMS

Поэтому всю повторяющуюся разметку мы вынесли в отдельный конфигурационный файл.

Пример конфигурации для разметки тестов
Пример конфигурации для разметки тестов

После этого стало гораздо лучше: минимальная разметка занимает всего одну строку. Разработчику достаточно указать tag_id и title для теста.

Такой разметкой разработчики пользуются с удовольствием
Такой разметкой разработчики пользуются с удовольствием

Внешний вид карточки в TMS практически не зависит от того, создана она вручную или выгружена из кода приложения. Отличий только два: у автоматически созданных карточек есть тег «откуда/выгружен/тест» и external ID, которые присваиваются системой. 

Что умеет делать TMS в Авито

Сейчас TMS и сервис выгрузки тест-кейсов из кода в Авито разделены. Также у нас есть отдельный сервис Event Gateway, который собирает ивенты от Git. Все сервисы взаимодействуют по API. 

Когда разработчик закоммитил новую функциональность и слил ветку в мастер, Git отправляет событие в Event Gateway — он создаёт сообщение в шине данных Kafka. Сервис выгрузки тестов получает сообщение от Kafka, делает Git clone новой версии мастер-ветки, парсит тесты и по API загружает тест-кейсы в TMS.

Схема работы TMS и сервиса выгрузки тестов
Схема работы TMS и сервиса выгрузки тестов

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

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

Визуализация аналитики тестовой модели. Зелёным отмечены автоматические тесты, красным — ручные
Визуализация аналитики тестовой модели. Зелёным отмечены автоматические тесты, красным — ручные

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

Минимум 70% тест-кейсов должны быть автоматизированы
Минимум 70% тест-кейсов должны быть автоматизированы

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

29 мая выгрузили очень много тест-кейсов разом
29 мая выгрузили очень много тест-кейсов разом

Ещё один график нужен больше для моей команды. Мы отслеживаем общее количество тест-кейсов, чтобы планировать развитие TMS. И главное — можно покрыть метриками качество всего продукта. На них мы опираемся, когда разрабатываем технические проекты по улучшению Авито.

Сейчас в каждом из проектов уже десятки тысяч тест-кейсов
Сейчас в каждом из проектов уже десятки тысяч тест-кейсов

Что даёт использование TMS

Какие итоги можно подвести:

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

    Но на старте, когда вы хотите проверить гипотезу и получить 80% результата за 20% работы, — это отличное решение. Также регулярные выражения хорошо подойдут, если вы используете один-два языка и обязательный код-стайл.

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

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

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

  5. Легко онбордить новичков: тестовая модель отображает функциональность приложения, как её тестировать и где она находится в коде.

Комментарии (4)


  1. laatoo
    11.11.2022 17:17

    разработчики пишут unit-тесты в коде приложения. А тестировщик на этапе планирования подсвечивает кейсы, которые нужно протестировать, разрабатывает критерии приёмки и принимает тесты на код-ревью

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

    Вы описали как работает ваша TMS, а как устроен процесс не понятно.

    Кажется, что тестировщики у вас вдруг стали формулировать требования к разработчикам (это немного за рамками их компетенций, разве нет?), а разработчики кроме юнит тестов стали ещё и отвечать за весьма экзотическую разметку тестов, чтобы кто-то там смог убедиться что выдуманные тестировщиками тест кейсы покрыты (что тоже, как кажется, немного не их дело, так как тест кейс разработчика != тест кейс тестировщика).

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


    1. CheshiRst2 Автор
      11.11.2022 22:12
      +2

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

      Одно из решений этой ситуации - это скрам подход. По скраму нету отдельной сущности QA. Там есть dev-team, владелец продукта, скрам-мастер и стейкхолдеры (заинтересованная сторона). И скрам постулирует, что в дев-команде все могут делать все, там нету деления на какие-то специальности, т.е. дев-команда вся целиком отвечает за качество, каждый ее участник, в этом вся суть. Если у нас, например, 3 бэка и 1 QA, это 4 участника дев-команды. И, для обеспечения качества, QA максимально шарит свою экспертизу внутри команды на всех этапах разработки.

      Давай пример. Вот надо сделать форму на странице, которая складывает два числа, это цель спринта.
      На этапе планирования, когда мы пишем критерии приемки, подключается QA и своей экспертизой сразу расписывает какие тесткейсы на каком уровне будут протестированы.
      Те тесткейсы, что будут автоматизированы (юниты на сложение, интеграционные на получение запроса от формы и е2е на полный тест формы) - пишут разработчики, QA помогает им и принимает тесткейсы на код-ревью.
      Те тесткейсы, что не будут автоматизированны (лучше их минимизировать) - руками тестирует сам QA инженер.
      Чтобы понимать, что вообще протестировано, на каком этапе, тесткейсы заносятся в ТМС. Ручные руками, автоматизированные - как выберете, у нас разметка и парсинг из кода. Это очень облегчает регрессионное тестирование в будущем. И в таком случае, практически любой участник команды сможет показать кусочек тестовой модели которая относится к этой форме. Качество уже можно начинать измерять.

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


      1. laatoo
        12.11.2022 10:37

        Если ответственный за качество QA, то почему он не может ставить требования разработке?

        В моем представлении требование разработке ставит бизнес, а QA разработке помогают, а не ставят требования. Это скорее игра в слова, смысл понятен :)

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

        1. Формулируется задача

        2. Оформляются требования

        3. QA оформляют тесткейсы на основе требований, заносят их в TMS

        4. Разработчики берут тесткейсы из TMS, работают над задачей

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

        6. В конце работы над задачей идет сверка, если все тесткейсы покрыты - задача выполнена

        ?


        1. CheshiRst2 Автор
          12.11.2022 21:35

          Это важная деталь :) QA не помогают, QA -инженер часть developers в скраме. И все команда developers отвечает за качество, сроки, выполнение критериев приемки.
          Процесс очень индивидуален в каждой команде. Процесс который ты описал тоже имеет место на жизнь, но лично мы так делаем редко. Пункт 3 обычно избыточен, двойная работа, тесты потом все равно выгрузятся (у нас :) ).
          Я бы описал примерно такой процесс:
          1. На спринт ставится задача сделать продуктовый инкремент какой-то (спринт-гоал)
          2. developers при планировании разбирают, как им задачу выполнить. Как будут использовать инкремент, ну туже самую форму, вероятно, разбивают на подзадачи. Вместе обдумывают что и каким способом будет протестировано (тут наш QA активно помогает).
          3. То, что было решено покрыть автотестами - разработчики пишут сами, QA ревьюит. При каком-то событии (тут неважно, мерж в мастер, создание ПР или что-то еще) - тесты из кода в выгружаются в TMS
          4. Когда все готово - инкремент проходит приемочное и/или исследовательское тестирование. Те тесткейсы, что при планировании были предусмотрены как ручные проверяются как раз тут. Все ручное - ручным способом попадает в TMS.
          5. Задача выполнена, когда все пункты Definition of done выполнены: критерии приемки удовлетворены, не менее 70% кода покрыто тестами, задача в проде и т.п.

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