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

Подчеркну, что это не методика и не стандарт, это ПРАКТИКА, и она подразумевает постоянную работу и вовлеченность всех членов команды. Зачем? Да чтобы не дожидаться конца проекта для проведения интеграции и внезапного всемирного коллапса. Кроме того задача CI — обезопаситься от разрушительных изменений в следствие рефакторинга, добавления нового функционала, изменений архитектуры и кучи других непредвиденных или известных проблем.

С помощью интеграционных сборок можно избавиться от синдрома «не знаю, на моей машине всё работает». Также мы защищаемся от «плохого кода», часто повторяющихся багов, «кривых» слияний. CI увеличивает возможности обратной связи потому, что она позволяет следить за состоянием проекта в течение дня.

Как мы пришли к непрерывности

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

Мы разрабатывали ядро, сервисные компоненты. Кроме того, нужно было протестировать производительность и установку-удаление компонентов. А также было требование к разработке – 100% покрытие кода юнит-тестами. В итоге у нас: автотесты, юнит-тесты, 4 тестовые среды, частично реализованные компоненты, «падающие» сборки и баги (куда без них?) и один тестировщик на 14 разработчиков. А хочется, чтобы всё само собиралось, устанавливалось, тестировалось и удалялось. И, конечно, очень хочется отдавать максимально стабильный и качественный результат основной команде тестирования.

Тестировать одному на 4-х системах, когда только у нас в команде 15 человек — мягко говоря, затруднительно. Поэтому мы решили попробовать подход Microsoft – Build-Deploy-Test. А успевать за разработкой тестировщику помогали следующие инструменты: TFS 2012, MSTest Manager 2012, Visual Studio 2012 Ultimate. Со временем перешли на 2013.

Мы решили сделать 2 CI сервера: Jenkins и TFS сервер. На первом у нас все собиралось раз в час в отладочном режиме (дебаг режиме). Затем происходила установка на сервер, прогон дымовых тестов (тесты, что приложение запускается) и после этого уже начиналась деинсталляция. Sonar запускал все модульные и интеграционные тесты раз в день, по ночам. Это решало проблему с нестабильностью сборки и скорейшего оповещения разработчиков о проблемах. На втором CI сервере (TFS build server) у нас сборка так же осуществлялась в отладочном режиме. Затем осуществлялась установка на тестовую машину и запуск функциональных автотестов. После того, как все тесты завершились (не важно, успешно или нет) происходила деинсталляция.

Мы решили сделать 2 сервера CI, чтобы отделить тесты разработчиков (модульные и интеграционные) от функциональных тестов тестировщиков. Сборки на Jenkins делались существенно чаще, чтобы реагировать на падающие тесты сразу. А сборки на TFS осуществлялись тестировщиком при необходимости и ежедневно по ночам, поскольку полный цикл функционального тестирования занимает много времени. Кроме того, чтобы всё строилось в Release конфигурации и код, как минимум, был всегда компилируемым, мы ввели Gated Check-in сборку. Когда программисты или тестировщик заливают изменения в TFS, сначала происходит сборка всей системы в Release конфигурации и изменения попадают в TFS только при условии его успешности. Если же есть проблема – изменения не применяются и сборка в TFS остается работающей.

Автоматизацию функциональных тестов мы начали с выбора их типов. Microsoft предлагает следующие:

  • Модульные тесты (unit test), которые пишут программисты.
  • Coded Ui Test – вид тестов, предназначенный для автоматизации функциональных тестов и тестирования пользовательского интерфейса.
  • Web Performance Test – функциональное тестирование веб-приложений в рамках организации нагрузочных тестов (Load Test).
  • Load Test – тесты для проведения нагрузочного тестирования веб-приложений.
  • Generic Test – специальный вид тестов, который позволяет выполнять внешние тестирующие приложения.
  • Ordered Test – позволяет организовать выполнение всех написанных автоматических тестов в определенном порядке.

В итоге, поразмыслив, мы решили, что нам больше подойдут generic и ordered тесты.

Generic тесту нужно указать *.exe файл и параметры, которые ему надо скормить. Каждый тест-кейс в TFS был связан с generic тестом. Для автотестов у нас было по одному проекту на каждый компонент и при запуске автотеста из TM, вызывается generic тест, который передает нужному *.exe файлу номер текущего автотеста, а дальше выполняется необходимый метод.

Процесс Build-Deploy-Test в целом выглядит следующим образом: разработчики делают изменения, перед заливкой изменений Build Server собирает всё в Release конфигурации и если сборка проходит, изменения заливаются на сервер.

image

Дальше по функциональному тестированию.

По Build-Deploy-Test подходу имеется 3 роли – Build Controller(BC), Test Controller(TC) и Test Agent (TA). У нас Build Controller совпадает с нашим Build Server и Test Controller.

Build Controller следит за сборками проекта, он собирает бинарники в папку сборки (Gated Check In тут не подходит, для тестирования были отдельные конфигурации сборки). Test Controller нужен для управлением запусками, хранения результатов тестирования. А Test Agent получает команду от ТС о запуске набора тестов, команду на развертывание и все параметры запуска, в том числе набор generic тестов.

Тестировщик открывает Microsoft Test Manager, выбирает там тест-план, который ему нужен, конфигурацию для запуска, сборку, которая будет использоваться, и тестовую среду. И всё! Остальное делает Тest Controller. Он получает набор параметров для тестового запуска (включая расположение папки с бинарниками) и запускает на тестовой среде установку. После того, как отработает скрипт установки, Test Agent запускает поочерёдно все выбранные тесты. После выполнения всех тестов запускается скрипт очистки, который удаляет приложение и, подчищая за собой тестовую систему, возвращает её в начальное состояние. Далее Тest Аgent передаёт управление Тest Сontroller, сообщая ему о своей готовности. Тest Сontroller забирает все временные папки тестового запуска, помещает результаты в TFS (их потом можно посмотреть через TM) и отображает результат тестов в TM – успешно выполненные тесты и не очень успешные.

Настройки тестовых сред

Все сборки у нас хранятся на Build Server. Там же находятся и generic тесты. После запуска автотестов в Microsoft Test Manager (TM), Test Controller создаёт на тестовой машине временную папку, куда копирует generic тесты и создаёт свой deploy.bat и clean.bat файлы. Эти скрипты состоят из 2-х частей. Первая — определение всех переменных запуска, в том числе и название директории сборки, а вторая часть – содержимое нашего deploy файла, который мы указали для этих тестовых настроек.

Таким образом, получается, что TC создаёт обертку для скрипта, который мы ему указали, как deploy скрипт (назовём его Call Deploy скрипт).

image

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

Этот скрипт уже выполняет все приготовления к установке, поскольку все скрипты реализуются в одной среде окружения, они имеют доступ к переменным, которые передал им TC. Используя их, скрипт обновляет конфигурацию и выполняет установку приложения. После завершения развертывания, к работе приступает Test Controller, который даёт задание Test Agent о выполнении тестов (тех самых generic тестов, которые он скопировал во временную папку). Эти generic тесты вызывают *.exe файл по указанному им пути и передают программе в качестве параметра номер тест-кейса. Автотест вызывает определенный метод в зависимости от переданного ему параметра.

После того, как все автотесты выполнены, Test Controller начинает процесс очистки. Для этого он вызывает скрипт-обёртку, которую создал вначале. Он определяет параметры и вызывает Call Clean скрипт, который лежит на всех тестовых системах. Call Clean запускает Clean скрипт, который выполняет деинсталляцию приложения, затем собирает все логи, архивирует их и перемещает во временную папку запусков, созданную Test Controller, чтобы после завершения очистки все логи загрузились в TFS и подцепились к запуску тестов.

У нас есть билды, которые по расписанию запускают Build-Deploy-Test подход. Для этого указывается специальный шаблон процесса в определении сборки. Раз в неделю происходит запуск такой сборки. В её настройке указано, какие тесты нужно выполнять, с какими настройками и на какой тестовой среде должны запускаться выбранные тесты.

Весь процесс начинается с запуска на Build Controller, который подает на Build Agent параметры для сборки (какие проекты собирать, в какой конфигурации – у нас это Release/Debug) После того, как Build Agent соберёт всё, Build Controller передаёт управление Test Controller вместе с параметрами для запуска автотестов – это набор автотестов, тестовая среда и тестовые настройки.

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

После запуска Test Controller он начинает процесс развертывания, затем передает на Test Agent тестирование и после этого Test Controller запускает скрипт очистки системы.

Если тестовая среда состоит из 2-х машин, то это должно быть указано в Test Manager, для них создается одна тестовая среда, в которой будет 2 машины. На каждой машине стоит свой Test Agent, подключенный к TFS, чтобы эти машины были видны в TM. За каждой машиной закрепляется отдельная роль, к примеру, Database Server и Клиент.

image

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

Проблемы при настройке Build-Test- Deploy процесса

Как оказалось, powershell скрипты не годятся для deploy/clean скриптов, для них подходят только cmd или bat. И, более того, если задать ему использование батника и из батника вызывать powershell скрипт, Test Manager игнорирует этот вызов. Точно так же он игнорирует и все sleep, wait и даже ping несуществующего хоста с таймером (нам нужен был sleep после остановки/запуска сервисов). Приходилось даже для таких простых вещей писать отдельные костыли, которые запускались из батника.

По мере эволюции проекта, эволюционировали и тесты. Сначала это был набор библиотек, поэтому можно было в скрипте развертывания просто копировать их и запускать тесты, а затем удалять их. И все были счастливы. Затем наши доблестные разработчики обернули всё в msi-пакеты, которые надо было запускать с множеством параметров. И удалять соответственно. Здесь уже задача усложнилась с изменениями параметров, которые надо подавать. Для этого мы использовали те параметры, которые Тest Сontroller передает Тest Аgent. Этого хватало.

Далее появилось много инсталляционных пакетов msi, которые надо ставить в определённой последовательности и с множеством параметров и конфигурационных файлов. Для того чтобы конфигурация проходила в зависимости от тестовой системы, мы подменяли все строки подключения со строки по умолчанию на нужную, подменяли все пути, параметры и прочее (всё это тоже брали из параметров запуска автотестов). Ещё нужно было сделать удаление MSMQ (Microsoft Message Queuing) очередей. Простыми батниками эту задачу не решить, поэтому я написала на PS такой скрипт. Но тут ждал очередной сюрприз – ну не хочет MicroSoft Test Manager работать со своим прославленным powershell. Непонятно почему. В итоге опять это решалось в самих автотестах.

Финальным (на данный момент) этапом установки стала одна единственная программка с пользовательским интерфейсом, в которой выбираются нужные компоненты и параметры и она уже сама запускает все необходимые файлы установщика с параметрами. Аналогично удаление. Мы автоматизировали установку без необходимости задавать значения в пользовательском интерфейсе. Установщику подается на вход сконфигурированный xml файл с нужными параметрами (частично использовались те, что можно передать с Test Controller) для установки и удаления. Всё остальное происходит так же.

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

Автор rsarvarova
Как Вы обеспечиваете непрерывную интеграцию?

Проголосовало 230 человек. Воздержалось 44 человека.

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

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


  1. KReal
    09.07.2015 15:01

    Интересно, почему в результатах опроса так сильно лидирует Jenkins и такой небольшой процент у TeamCity. Мы вот с TFS думаем на TeamCity перелезать.


    1. ReanGD
      09.07.2015 15:31

      Как минимум TeamCity сильно платный, то, что они дают бесплатно очень быстро перестанет хватать


    1. freehome
      09.07.2015 16:11

      У Jenkins много плагинов.


    1. DmitryVasiliev
      09.07.2015 17:12

      Мы начали с TeamCity, но быстро пришли к тому, что бесплатной версии нам не хватает — число сборщиков там очень ограничено, а дополнительные стоят денег. Причем немалых. При количестве серверов порядка 35 бесплатный Jenkins нам подходит гораздо больше :)


  1. lair
    09.07.2015 16:12

    Другое: AppVeyor.


  1. saboteur_kiev
    09.07.2015 22:48

    > Все сборки у нас хранятся на Build Server.
    В случае большой команды, это может быть проблемой для Jenkins

    Web UI работает на собственной реализации веб-сервера, написанной на java, и если прямо через его web gui много разработчиков будет скачивать крупные файлы билдов, это будет сильно нагружать CPU.

    У нас 1000+ разработчиков, и в какой-то момент jenkins начал жутко подтормаживать (с разработкой появлялись новые платформы, количество билдов и скачиваний выросло), причину тормозов обнаружили буквально ad-hoc тыканием (Версию Jenkins используем последнюю LTS).

    В общем в виде воркэраунда, просто стали все build артефакты экспортить в отдельную папку, которую раздаем по фтп, а в самом дженкинсе в job-е прописали post-job создание дескрипшена с ссылкой на эту папку. Теперь все довольны.


  1. Stasik0
    10.07.2015 08:59

    Другое: travis-ci


  1. lany
    10.07.2015 11:15

    Hudson — это Jenkins или другое?


    1. saboteur_kiev
      10.07.2015 12:47

      Jenkins это форк от Hudson-а. Hudson стал закрытым и многие свалили пилить Jenkins


      1. lany
        10.07.2015 12:51

        Спасибо, я знаю. Я спрашивал, за какой вариант голосовать.