Pull to refresh

Continuous integration and code metrics

Reading time7 min
Views8K
В этой статье рассматривается настройка continuous integration процесса с метриками кода. Предполагается работа с java кодом и библиотеками: junit, cobertura, findbugs. В качестве системы сборки используем ant, а сам процесс будет управляться из cruise control. В качестве scm будет немного git'а.

Что мы получим в итоге? Ява проект с ant'овским сценарием сборки. Итогом сборки будет откомпилированный проект и набор метрик: отчеты по junit тестам, процент покрытия кода тестами и отчет о потенциальных ошибках. В дополнение, весь процесс сборки будет проходить ежедневно в автоматическом режиме и вся история метрик сохраняется для групповых отчетов.
image


Исходный материал


В качестве исходной точки возьмем любой java проект. Проект должен содержать хотя бы несколько классов, с несколькими unit тестами. При этом не надо покрывать 100% кода — что бы не видеть абсолютные значения в отчетах.
Пример уже готового проекта можно скачать ТУТ

Что нам надо и где скачать


Предполагаем у вас уже стоит java, если нет, то самое время установить пакет.
Установите ant. Скачайте ТУТ, распакуйте и добавьте в PATH путь к ant/bin

JUnit качаем ТУТ и помещаем junit.jar в ant/lib
Findbugs качаем тут, распаковываем и добавляем finbugs-ant.jar в ant/lib
Cobertura отсюда, и просто распаковываем на винт.

Настройка билда


Во первых настроим компиляцию проекта с командной строки. Для этого в корне проекта создадим build.xml и в него поместим

<target name="compile" depends="init">
<javac destdir="bin" debug="yes">
<src path="src" />
</javac>
</target>


Мы определяем цель «compile», и можем ее выполнить, вызвав в командной строке
ant compile
Атрибут depends заставляет выполнить цель init до compile. Предварительная цель создает все необходимые директории.

Добавление тестов


Как многие уже знаю, в современном мире разработки ПО нельзя обойтись без unit тестирования. Наш тестовый проект не исключение. Он содержит несколько JUnit тестов на часть функциональности.
Автоматическая сборка должна прогонять все тесты и писать отчет: сколько тестов всего, сколько выполнилось успешно.
При работе с крупными заказчиками встречается требование о проценте покрытия кода тестами. Для вычисления этой метрики мы будем использовать cobertura.
Cobertura может работать как standalone приложение, генерируя html отчеты. Но для автоматизированного тестирования мы будем использовать ant plugin и отчетность в формате xml.
Несколько слов о механизме вычисления покрытия.
Во первых есть два типа покрытий: покрытие линий кода, и покрытие веток. Покрытие по линиям просто считает общее количество строк проекта, и сколько из них было затронуто тестами. Покрытие веток учитывает ветвление кода.
Для подсчета статистики по покрытию мы должны скомпилировать код особым образом, так чтобы cobertura добавила в него свои маркеры. Во время работы junit эти маркеры вычисляют покрытие.
Добавление маркеров — процесс крайне простой, достаточно добавить в classpath библиотеки cobertura.
Сделаем это.
Предыдущая ant target просто компилировала проект, след цель делает тоже самое, но с маркерами и сохраняет классы в отдельную папку:

<target name="cobertura" depends="compile">
<javac destdir="bin-cob" debug="yes">
<classpath refid="cobertura.classpath" />
<src path="src" />
</javac>
<cobertura-instrument>
<fileset dir="./bin-cob">
<include name="**/*.class" />
<exclude name="**/*Test.class" />
</fileset>
</cobertura-instrument>
</target>


Кроме этого мы исключаем классы тестов из подсчета покрытия.

Следующий шаг это тестирование:

<target name="test" depends="cobertura">
<junit fork="yes" printsummary="yes">
<sysproperty key="net.sourceforge.cobertura.datafile" file="cobertura.ser" />
<classpath refid="cobertura.classpath" />
<!-- Note! Test are run against classes compiled with cobertura marks -->
<classpath refid="classpath.bin-cob" />
<formatter type="xml"/>
<batchtest fork="yes" todir="reports">
<fileset dir="./src">
<include name="**/*Test.java"/>
</fileset>
</batchtest>
</junit>
<!-- make xml report -->
<cobertura-report srcdir="${src.dir}" destdir="reports" format="xml" />
<!-- make html report -->
<cobertura-report destdir="coveragehtml">
<fileset dir="./src">
<include name="**/*.java"/>
</fileset>
</cobertura-report>
</target>


Эта target выполняет два шага: тестирует код и генерирует отчет по покрытию.
Благодаря маркерам, cobertura пишет процесс выполнения тестов в файл cobertura.ser. Следующий шаг использует ser для генерации xml отчета в папку reports. Так же генерируется html отчет.

Добавление детектора ошибок


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

Findbugs это система для анализа кода на ошибки программирования, она замечает дублирование кода, не правильное оформление методов и тп.

Добавим еще один target:

<target name="findbugs" depends="test">
<findbugs home="${findbugs.home}"
output="xml"
outputFile="reports/findbugs.xml" >
<sourcePath path="./src/" />
<class location="./bin/" />
</findbugs>
</target>


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

Сохранение истории


На этот момент у вас есть готовый build.xml, с сборкой проекта и метриками. При каждой сборке система будет делать новый build и переписывать отчетность. Донастроим систему для ведения истории изменений. Для этого будем использовать веб сервис codemetric.org и наши отчеты будут доступны в виде: demo.codemetric.org

image

Во первых, надо зарегистрироваться на www.codemetric.org, создать space, группу и build. После этого в вашем профиле появится имя билда его ID и token. Эти числа вместе с вашим id нужны для конфигурирования очередного ant plugin
Во первых скачайте plugin для сервиса отчетов и запишите его в lib вашего ant.
http://www.codemetric.org/codemet.jar

Добавим еще одну target:

<taskdef name="publish" classname="org.codemetric.ant.Publish"/>
<target name="success">
<publish
user="123456"
build="234567"
token="345678"
reports="reports"
/>
</target>


Укажите user, build и token значения с вашего профайла. reports — это путь к отчетам, сгенерированным ранее.
Выполним нашу цель
ant success
Сценарий должен подтвердить сохранение истории на сервер, после чего вы можете увидеть ее в вашем .codemetric.org

Уйдем в cruise


Итак. На данный момент у нас есть ява проект с сценарием сборки, которая выполняет тесты, вычисляет метрики и сохраняет историю. Единственное неудобство, что это происходит в ручном режиме.
Давайте этот процесс автоматизируем.
Continuous integration это практика автоматической сборки проекта на основе некоего расписания — например раз в день. Т.о. вся команда делает успешные коммиты, и сервер может в конце дня проверить отсутствие конфликтов и тп.
Подробнее о CI можно прочитать в wikipedia.

Настроим CruiseControl на ежеминутную проверку нашего проекта.
Сценарий работы такой:
  • забрать изменения из некого репозитория
  • проверить наличие изменений
  • выполнить сборку
  • в случае успеха, отослать отчет


Напомним, что у нас есть: есть проект, есть скрипт сборки, есть цель для публикации отчета. И нам не хватает некого центрального репозитория.
В принципе, подойдет любая scm система, но мы опробуем git.
Считаем, что git у вас уже установлен
Скопируйте ваш проект в корень диска.
Зайдем в /testproject и выполним
git init
— это создаст новый репозиторий. Проект должен содержать только папку src и build.xml
Добавим все файлы проекта:
git add
git commit -m "init import"

Теперь у вас есть ваш репозиторий с проектом внутри. Перейдем к настройке cruisecontrol.
Во первых, скачайте его тут и распакуйте.
В папке projects надо создать рабочую копию ваше проекта средствами git. Выполните в папке projects
git clone /testpoject
Вы увидите новую рабочую копию. Теперь можно перейти непосредственно к написанию конфигурации для cruise.
Откройте файл config.xml в папке cruise и удалите или закомментируйте проект доступный по умолчанию. Итоговый файл конфигурации такой:

<cruisecontrol>

<project name="testproject">

<bootstrappers>
<gitbootstrapper localWorkingCopy="projects/${project.name}/" />
</bootstrappers>

<modificationset quietperiod="30">
<git LocalWorkingCopy="projects/${project.name}"/>
</modificationset>

<schedule interval="60">
<ant anthome="/home/andrew/temp/apache-ant-1.7.1" antWorkingDir="projects/${project.name}/"/>
</schedule>

<publishers>
<onsuccess>
<antpublisher anthome="/home/andrew/temp/apache-ant-1.7.1" target="success" antWorkingDir="projects/${project.name}/" />
</onsuccess>
</publishers>

</project>
</cruisecontrol>


Опишем, что же делает каждая группа:
Bootstrappers выполняется перед циклом сборки и фактически забирает изменения из родного репозитория.
После этого modificationset проверяет, если файлы в локальной копии были изменены. Если изменений небыло, то сборка будет отложена.
Schedule это фактический планировщик — bootstrappers и modificationset автоматически выполняются перед шагами описанными в schedule.
Последняя группа publishers. Она зависит от результата сборки, и выполняется в случае успеха. В идеале, нужно прописать логику для репорта ошибок — например письмо team lead.

п.с.

Если вы хотите использовать другую scm (cvs, svn) Достаточно пометять bootstrapper и modificationset.

Подробнее о всех опциях cruise можно почитать тут.

Andrew Romanenco
www.romanenco.com
Tags:
Hubs:
+18
Comments8

Articles