Смена тестирования влево с модульными тестами

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

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

Graphic that describes arguments about adopting unit testing.

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

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

Тестирование таксономии DevOps

Определение таксономии теста является важным аспектом процесса тестирования DevOps. Тест DevOps классифицирует отдельные тесты по их зависимостям и времени их выполнения. Разработчики должны понимать правильные типы тестов для использования в разных сценариях и тесты различных частей процесса. Большинство организаций классифицируют тесты на четырех уровнях:

  • Тесты L0 и L1 — это модульные тесты или тесты, которые зависят от кода в сборке при тестировании и ничего другого. L0 — это широкий класс быстрых модульных тестов в памяти.
  • L2 — это функциональные тесты , которые могут потребовать сборки и других зависимостей, таких как SQL или файловая система.
  • Функциональные тесты L3 выполняются для тестируемых развертываний служб. Для этой категории тестирования требуется развертывание службы, но может использоваться заглушки для зависимостей ключевых служб.
  • Тесты L4 — это ограниченный класс тестов интеграции, выполняемых в рабочей среде. Для тестов L4 требуется полное развертывание продукта.

Хотя это было бы идеально для всех тестов, которые будут выполняться в любое время, это не возможно. Teams может выбрать место в процессе DevOps для выполнения каждого теста и использовать стратегии shift-left или shift-right для перемещения различных типов тестов более ранних или более поздних версий в процессе.

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

Рекомендации по модульной проверке

Задайте строгие рекомендации для модульных тестов L0 и L1. Эти тесты должны быть очень быстрыми и надежными. Например, среднее время выполнения на тест L0 в сборке должно быть меньше 60 миллисекунда. Среднее время выполнения на тест L1 в сборке должно быть меньше 400 миллисекунда. Тест на этом уровне не должен превышать 2 секунды.

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

Chart that shows continuous focus on test execution time.

Рекомендации по функциональному тесту

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

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

Принципы тестирования DevOps

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

Diagram that shows an example of a quality vision and lists test principles.

Сдвиг влево для тестирования ранее

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

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

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

Diagram that shows the move to shift-left testing.

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

Написание тестов на самом низком возможном уровне

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

Цель проверки надежности

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

Написание функциональных тестов, которые могут выполняться в любом месте

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

Разработка продуктов для тестирования

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

Рассматривать тестовый код как код продукта

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

Использование инфраструктуры общих тестов

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

Создание владелец кода ответственных за тестирование

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

Пример: сдвиг влево с модульными тестами

Команда Майкрософт решила заменить устаревшие наборы тестов современными модульными тестами DevOps и процессом влево. Команда отслеживала ход выполнения по тринекли спринтам, как показано на следующем графике. График охватывает спринты 78-120, которые представляют 42 спринта в течение 126 недель или около двух с половиной лет усилий.

Команда начала работу с 27K устаревших тестов в спринте 78 и достигла нуля устаревших тестов на S120. Набор модульных тестов L0 и L1 заменил большинство старых функциональных тестов. Новые тесты L2 заменили некоторые тесты, и многие старые тесты были удалены.

Diagram that shows a sample test portfolio balance over time.

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

Начало работы

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

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

Команда не сосредоточится на написании новых тестов L2 до спринта 101. В то же время число тестов TRA снизилось с 27 000 до 14 000 с Sprint 78 до Sprint 101. Новые модульные тесты заменили некоторые тесты TRA, но многие из этих тестов были просто удалены на основе анализа полезности команд.

Тесты TRA выросли с 2100 до 3800 в спринте 110, так как в исходном дереве обнаружены дополнительные тесты и добавлены в граф. Оказалось, что тесты всегда выполнялись, но не отслеживались должным образом. Это был не кризис, но было важно быть честным и переоценить по мере необходимости.

Ускорение работы

После того как команда имела сигнал непрерывной интеграции (CI), который был чрезвычайно быстрым и надежным, он стал доверенным индикатором качества продукта. На следующем снимке экрана показан запрос на вытягивание и конвейер CI в действии, а также время, необходимое для прохождения различных этапов.

Diagram that shows the pull request and rolling CI pipeline in action.

Это займет около 30 минут, чтобы перейти от запроса на вытягивание для слияния, который включает выполнение 60 000 модульных тестов. Из слияния кода в сборку CI составляет около 22 минут. Первый сигнал качества от CI, SelfTest, приходит примерно через час. Затем большая часть продукта тестируется с предлагаемым изменением. В течение двух часов от слияния до SelfHost весь продукт тестируется, и изменение готово к переходу в рабочую среду.

Использование метрик

Команда отслеживает оценку карта как показано в следующем примере. На высоком уровне оценка карта отслеживает два типа метрик: здоровье или долг, а также скорость.

Diagram that shows a metrics scorecard for tracking test performance.

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

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

Метрики скорости проектирования измеряют скорость в разных частях конвейера непрерывной интеграции и непрерывной доставки (CI/CD). Общая цель заключается в увеличении скорости конвейера DevOps: начиная с идеи, получения кода в рабочую среду и получения данных от клиентов.

Следующие шаги