Автоматизированное тестирование для CI/CD

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

Задача непрерывной интеграции и развертывания (CI/CD) — обеспечить частую поэтапную реализацию изменений, чтобы разработчики регулярно получали отзывы о своих продуктах и услугах. Однако более быстрая и частая доставка ПО не должна негативно сказываться на качестве. Пользователи могут ратовать за новые возможности, однако они ожидают, что ПО продолжит функционировать и останется стабильным.

Именно поэтому надежное и тщательное автоматизированное тестирование, которое позволяет гарантировать качество новых сборок, — необходимая часть CI/CD-процесса.

Зачем автоматизировать CI/CD-тестирование?

Тесты дают нам уверенность в качестве нашего ПО и давно стали частью процесса разработки.

В каскадной модели ручное тестирование или этап QA выполняется после развертывания и интеграции кода.

💡Примечание. Каскадная стратегия, или каскадная модель, — линейный процесс последовательной разработки ПО и управления проектами. Это одна из самых первых моделей разработки программного обеспечения. Ее отличает структурированный и системный рабочий процесс, разбитый на четкие этапы.

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

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

Чтобы успешно внедрить DevOps-методологию, необходимо регулярно делать коммиты, а затем выполнять сборку и тестирование кода, в идеале — несколько раз в день. Однако даже в небольшой команде выполнять полный набор тестов вручную каждый день, а то и не по одному разу, совершенно нереально. Поэтому неотъемлемой частью любого CI/CD-пайплайна становится автоматизированное тестирование.

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

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

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

Место тестирования в CI/CD-процессе

Автоматизированное тестирование выполняется на нескольких стадиях пайплайна.

Суть CI/CD и автоматизации тестирования заключается в коротких циклах образной связи, позволяющих вашей команде как можно раньше узнавать об имеющихся проблемах.

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

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

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

К приоритизации создания и запуска автоматизированных тестов удобно подходить с позиции тестовой пирамиды.

Создание пирамиды тестирования

Пирамида тестирования — инструмент приоритизации автоматизированных тестов CI/CD-пайплайне, позволяющий определить нужное количество тестов и порядок их выполнения.

В основании пирамиды тестирования, которую первоначально ввел Майк Кон, находятся юнит-тесты, в середине — интеграционные, наверху — тесты интерфейса.

Пирамида тестирования состоит из следующих этапов:

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

Какие типы CI/CD-тестов стоит включить? Давайте рассмотрим варианты.

Юнит-тесты

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

В командах, которые используют юнит-тесты, их, как правило, пишут разработчики — параллельно с написанием самого кода. Это часть методологии разработки через тестирование (Test Driven Development, TDD). Однако, чтобы писать юнит-тесты, вовсе не обязательно следовать TDD.

Юнит-тесты можно рассматривать как часть задачи по разработке ПО. В этом случае вы проверяете их наличие при ревью кода или с помощью отчетов о покрытии кода.

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

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

Интеграционные тесты

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

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

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

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

Сквозные тесты

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

Автоматизированные сквозные тесты можно выполнять не только через пользовательский интерфейс, но и через API, которое тоже работает с несколькими компонентами системы (API также проверяют при помощи интеграционных тестов).

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

Разработка через поведение

Разработка через поведение (behavior-driven development, BDD) — подход к разработке программного обеспечения, предполагающий тесное сотрудничество между разработчиками, тестировщиками и руководителями. Это развитие подхода TDD, где упор делается на поведение ПО, а не на его реализацию.

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

  • Упор на поведение: в BDD основное внимание уделяется описанию поведения на простом языке. Часто такие примеры пишутся в формате «Дано — Когда — Тогда» (Given-When-Then), который описывает исходное состояние, действие и ожидаемый результат.
  • Совместная работа: BDD стимулирует совместную работу разработчиков, тестировщиков и руководителей, позволяющую сформировать единое понимание требований. Этот процесс помогает прояснить ожидания и избавиться от взаимного недопонимания.
  • Исполняемые спецификации: сценарии BDD пишутся таким образом, что их можно автоматически выполнять как тесты. Для написания сценариев в понятном человеку формате обычно используются такие инструменты как Cucumber, SpecFlow и JBehave. В дальнейшем сценарии можно связать с автоматизированными тестами.
  • Документация: сценарии служат одновременно тестами и документацией и дают четкое и актуальное описание требований к поведению системы. Благодаря этому проще понять особенности системы и требований к ней.
  • Итеративный подход к разработке: BDD поддерживает итеративную и инкрементальную разработку, стимулируя короткие циклы обратной связи и доработки ПО. Каждая итерация предполагает написание и реализацию новых сценариев, чтобы ПО развивалось в соответствии с потребностями бизнеса.

Тестирование производительности

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

Под тестированием производительности понимается целый ряд стратегий тестирования для проверки того, как ваше ПО будет вести себя в реальной среде:

  • Нагрузочное тестирование проверяет поведение системы при росте спроса.
  • Стресс-тестирование намеренно превышает ожидаемый спрос.
  • Тестирование на выносливость замеряет производительность при продолжительной высокой нагрузке.

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

Тестовые окружения

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

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

Каким образом разработка через тестирование поддерживает автоматизацию тестов?

Разработка через тестирование — подход к разработке, выросший из экстремального программирования (extreme programming, XP). Первый шаг при внедрении TDD — создание списка тест-кейсов для функциональности, которую вы хотите добавить. После этого нужно поочередно написать для каждого тест-кейса тест, завершающийся ошибкой, а затем код, который сможет пройти этот тест. Затем выполняется необходимый рефакторинг кода, и вы переходите к следующему тест-кейсу. Кратко этот процесс можно описать так: «Ошибка, успех, рефакторинг» или «Не только работающий, но и красивый код».

Одно из основных преимуществ TDD — вам обязательно придется создавать автоматизированные тесты для всего нового кода. В результате покрытие кода постоянно растет, а при всех изменениях вы регулярно получаете быструю обратную связь. Другие выгоды разработки через тестирование:

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

TDD — эффективный способ расширить покрытие автоматическими тестами для обеспечения CI/CD-пайплайна. При этом TDD не является обязательным условием эффективной DevOps-стратегии: поддерживать высокий уровень покрытия автоматизированными тестами можно и другими путями.

Обработка обратной связи

Задача запуска автоматизированных тестов в рамках CI/CD в том, чтобы быстро получать обратную связь по изменениям в коде. Поэтому важно следить за поступающей информацией и реагировать на нее. Чтобы максимально эффективно использовать автоматизированное тестирование, внедряйте рекомендованные практики:

  • Упростите доступ к результатам тестов. CI-серверы, как правило, интегрируются с инструментами для автоматизированного тестирования, чтобы вы могли просматривать все результаты на одной панели управления или радиаторе.
  • Интегрируйте отчеты о тестировании с коммуникационной платформой, например Slack, чтобы получать автоматизированные уведомления о результатах тестирования.
  • Если тест завершился ошибкой, постарайтесь как можно скорее найти причину и устранить ее, пока сверху не надстроен другой код. В этом вам помогут CI-инструменты, которые определяют фрагмент кода, к которому относится тест, и дают доступ ко всем произведенным им данным: трассировке стека, выходным значениям, скриншотам и т. п.
  • Проектируйте тесты так, чтобы каждый тест проверял что-нибудь одно, тогда вы сразу поймете, в чем заключается ошибка.

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

Непрерывное и автоматизированное тестирование

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

Непрерывное тестирование — это практика запуска полного набора автоматизированных тестов в рамках CI/CD-пайплайна. При автоматизированном тестировании каждый набор изменений в коде автоматически проходит серию автоматизированных тестов, чтобы как можно скорее найти все возможные ошибки.

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

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

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

Означает ли CI/CD и автоматизация тестирования конец эпохи ручного тестирования?

Среди тех, кто только начал работать с CI/CD, распространено заблуждение о том, что автоматизированное тестирование устраняет необходимость в ручном тестировании и в профессиональных тестировщиках.

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

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

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

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

Непрерывное улучшение авто-тестов

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

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

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

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

Автоматизированное тестирование для CI/CD: подводим итоги

Автоматизация тестирования занимает центральное место в любом CI/CD-пайплайне. Автоматический запуск тестов обеспечивает быструю и надежную обратную связь по изменениям в коде. Это в свою очередь повышает эффективность разработки, поскольку чем раньше обнаружены ошибки, тем проще их устранять.

Рекомендуется выстраивать порядок автоматизированных тестов в зависимости от продолжительности их выполнения. Сначала следует запускать юнит-тест, поскольку они дают самые быстрые результаты, затем интеграционные тесты и только после этого сквозные. Если у вас еще нет автоматизированных тестов, начинать проще всего с юнит-тестирования. Разработка через тестирование (TDD) — проверенный подход к разработке, который помогает расширить покрытие кода юнит-тестами и поддерживать его.

По мере развития DevOps-культуры в вашей команде вы сможете перейти к непрерывному тестированию. В рамках этого перехода вам нужно будет автоматизировать создание и обслуживание тестовых окружений. При написании высокоуровневых автоматизированных тестов рекомендуем в первую очередь делать их для тех подсистем, где наиболее велик риск ошибок. В таких случаях необходимы автоматизированные тесты производительности, например, нагрузочное и стресс-тестирование, а также тестирование на выносливость. Ручное исследовательское тестирование — отличный способ найти пробелы в покрытии кода и тем самым улучшить CI/CD-пайплайн.

Как вам поможет TeamCity

TeamCity предлагает широкую поддержку тестовых фреймворков и ряд функций для автоматизации тестирования, которые помогут максимально эффективно использовать CI/CD-процесс.

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

TeamCity интегрирован с баг-трекерами, IDE, Slack и другими платформами, так что вы можете получать уведомления о найденных ошибках прямо по ходу работы. Наконец, полная поддержка виртуальных машин и контейнеров Docker позволяет автоматизировать управление тестовыми окружениями и внедрить непрерывное тестирование в рамках CI/CD-пайплайн.