Описание статьи


Необходимо ли компании выделять компетенции автоматизатора и так ли они уникальны?


В статье описано, как внутри компании при действующих командах автоматизации происходил пересмотр компетенций, в результате которых возник парадокс «Команда есть, а компетенций нет». Результом пересмотра стало то, что команд автоматизации больше нет.


Цель статьи


Рассказать опыт и причины принятия тех или иных решений.


WARNING: Все решения были проработаны в рамках определенной компании, учитывая специфику проектов

Структура статьи


  • Описание тестируемого объекта, проекта, команд;
  • Формулирование целей для системы с автотестами и их решение;
  • Формулирование компетенций, необходимых для достижения поставленных целей;
  • Подведение итогов.

Описание тестируемого объекта, проекта, команд


Стек: С#, SpecFlow(BddFy), Selenium


Тестируемый объект: Системы для автостраховых компаний в Америке. У всех систем есть админки для настройки приложений, rest api, чтобы клиенты могли сами писать порталы, приложение для создания полиса и осуществоения платежей, приложение для генерации отчетов. Все части должны быть покрыты тестами.


До определенного момента команда автоматизации была обособленной, практически не взаимодействующей с другими командами. Перед ней стояла цель максимально покрыть регрессионный набор автотестами, чтобы уменьшить срок проведения полного регресса QA. Перед регрессом создавался тест ран, который должен был пройден для выпуска версии релиза. Результаты прогона автотестов мапились в этот ран. В системе было примерно 4000 кейсов, которые запускались на разных environment. Время полного прогона больше двух суток. Порядка 15% имели статус broken(сломаны по вине автотеста).


Цели


Появилась необходимость в быстрой верификации качества вмерживаемой фиче-ветки в main branch. Отсюда возникли следующие цели:


  • Стабильные тесты. Если падают тесты, то это свидетельствует о том, что влитая ветка имеет сломанный функционал и команда, которая вливает/влила, должна её исправить;
  • Быстрые тесты. Сократить время прогона до 4 часов. Мы пришли к выводу, что 4 часа на полный прогон тестов допустимое время ожидания (48 изначальных часов — нет).
  • Необходимо сделать фундамент под мутационные тесты. Этому будет посвящен отдельный цикл статей

Быстрые и стабильные тесты


Верхнеуровневно я решил объединить два пункта, поскольку увеличения скорости прогона можно добиться за счет параллелизации, а это открывает ящик пандоры с флаки тестами. Ведь, если у вас есть некий статический контекст по типу HttpContext, который хранит информацию о залогиненном юзере и его claims, то при параллельном запуске этот логин будет выполняться для всех запущенных тестов. Что, при определенных условиях, приводит к падениям, которые локально будет довольно сложно воспроизвести. Теперь остановимся на каждом из пунктов поподробней.


Быстрые тесты


Для увеличения скорости прогонов тестов были реализованы следующие подцели:


  • Параллельное исполнение тестов;
  • Написание эмулятора, который бы напрямую через код мог привести систему к тестируемому состоянию;

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


  1. Переписывание CI скриптов. Выбор был сделан в пользу Cake(C# Make), поскольку он использовался в основном приложении и содержал весь необходимый функционал. Добавлена возможность для проброса дополнительного параметра, задающего количество потоков;
  2. Выкошены практически все статические классы методы, за исключением extension-ов;
  3. Добавлена связка Selenoid с GoGrid Router – чтобы снизить нагрузку на CI/CD агентах.

В рамках написания эмулятора:


  1. Перенос проектов автотестов в solution с тестируемым объектом;
  2. Подключение solution analyzer и исправление варнигов и ошибок;
  3. Перенос общих частей в MSBuild конфигурации;
  4. Написание эмулятора.

От себя бы хотелось добавить, что эмулятор писать имеет смысл, если в Controller/ View тестируемого объекта не содержится логики и у приложения есть DI контейнер. В противном случае перед тем, как писать эмулятор, лучше всю логику вынести в сервисы/хэндлеры. Эмулятор позволяет без поднятия браузера или обращения по Rest выполнить нужные действия. Тут я не устану повторять: «данные действия не являются тестируемыми, они лишь приводят систему в тестируемое состояние». Если по сравнению с рест или шиной выигрыш по времени не такой большой, то если сравнивать с UI — выигрыш очень существенный. Вынос повторяемых степов в эмулятор позволил сократить время прогона теста до 5 раз.


Cтабильные тесты


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


  1. Использование DI контейнера. DI контейнер стал Must-Have feature после параллелизации, с ним стало возможно управлять скоупами и можно быть уверенным, что разным тестам приедет разная или одна и та же реализация, в зависимости от типа регистрации. Это позволило существенно поднять стабильность и уменьшить стоимость поддержки;
  2. Отказ от инжектируемого ScenarioContext. Когда много тестов, нужен иммутабельный контекст. Невозможно быть уверенным, что тебе доезжает нужное значение, если есть степы, которые его могут изменить. На что заменили будет подробней рассмотрено в пункте про мутационные тесты;
  3. Проведение замеров для вычисления оптимальной нагрузки. Для того, чтобы сделать тесты атомарными, были написаны скрипты для клонирования сложных сущностей на уровне БД — при больших нагрузках SQL Server становилось плохо и тесты могли падать;
  4. Попытались внедрить принципы SOLID насколько сил хватило. Этот пункт влияет на стабильность косвенно, данные принципы помогают убрать дублируемую логику и сделать код более читабельным.

Большую часть времени я был уверен, что нестабильные тесты – вина разработчика (автоматизатора), который их написал. Время показало, что исправить все тесты не получается. Нам удалось исправить 99.9 % тестов, но время, потраченное на увеличение порядка стабильных тестов, увеличивалось экспоненциально. И в один момент мы поняли, что 3 падающих теста на 4000 уже не так критично и их можно пройти руками. Да, так не выйдет сделать полностью автоматическую доставку релиза до staging, но пока это не требуется.


Фундамент под мутационные тесты


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


  • Реализации шагов должны быть максимально тонкими, чтобы можно было максимально быстро сменить BDD Framework. SpecFlow хороший инструмент, но уж очень медленно он осуществлял миграцию на .net core. Да и если понадобится расширение поведения степа, у SpecFlow есть только хуки, а они удобны не всегда;
  • Адаптация CQRS подхода под нужды автоматизации. Получилось разбиение на UserActivity(UserActivity, UserActivityHandler, UserActivityResult) и CheckAssertions (CheckAssertions, CheckAssertionsHandler);
  • Реализация Command Dispatcher(Mediator) с возможностью расширения. Это имено та, часть которая отвечает за расширяемость системы.

Для наглядности приведу пример:
Реализация степ биндинга выглядит следующим образом:


image


Медиатор находит подходящий UserActivityHander и вызывает у него Handle:


image


Но, поскольку Command Dispatcher расширяемый, следовательно можно:


  1. Перед вызовом Handle найти валидаторы, которые проверяли, что система находится в
    валидном состоянии;
  2. Сохранять результаты всех UserActivity в отдельный контекст, который инициализируется для каждого теста.

Все это позволяет быть уверенным, что если появятся новые требования (например, формирование популяций для мутаций) систему не придется переписывать, а просто появится новое поведение.


Компетенции


После того, как руководители отделов составили матрицы компетенций, выяснилось что компетенции, которые требовались автоматизаторам, большей частью повторялись с компетенциями разработчиков(CLR, принципы разработки ПО, паттерны, средства мониторинга, DI), а те, что не повторялись(Selenium, Selenoid, Specflow), при условии, что человек умеет читать документацию, становятся некритически важными.


Итог


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


  1. Имплементация новой feature не считается завершенной, если на добавленный функционал не написаны автотесты;
  2. Нельзя мержить ветку в main branch, если в ней падают тесты;
  3. "Ever green main branches".

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


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