8 February 2011

Встраиваем сбор Code Coverage в CruiseControl.NET

.NET

Введение


Последнее время активно пропагандируется практика разработки программного обеспечения Test-Driven Development. Бесспорно, она очень полезна, но не всегда и не все ее применяют. Поэтому часть кода покрыта юнит-тестами, а часть остается непокрытой. Проследить за каждым проектом вручную, нормально написаны там тесты или нет, является практически невыполнимой задачей.
Недавно я задался вопросом, а как можно автоматизировать процесс сбора метрики, которая показывает процент покрытия кода тестами. Было решено встроить ее сбор в CruiseControl.NET. Естественно, что 100% покрытие не гарантирует отсутствие багов, но хотя бы показывает отношение разработчиков к написанию тестов.

image


В данной статье я не буду останавливаться на моментах касающихся настройки CruiseControl’а для сборки проектов и запуска юнит-тестов. Будут описаны шаги, которые позволят собрать необходимую информацию о покрытии кода и вывести ее на странице с тестами. Для написания юнит-тестов наша компания использует фреймворк от Microsoft – MSTest. Результаты работы с этим фреймворком и будут описываться в статье. Стоит заметить, что необходимым условием для встраивания покрытия кода является изначальная настройка CruiseControl'а для запуска тестов.

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

Всё нижеописанное относится к проектам, написанным в Visual Studio 2010. Отличия с 2008 версией хоть и небольшие, но они есть.

Создание data.coverage файла на билд-сервере


Visual Studio позволяет настроить выполнение тестов таким образом, чтобы после прохождения всех тестов создавался файл, в котором будет храниться информация о покрытых участках кода. Называться этот файл будет data.coverage (хранится он в папке In с результатами прохождения тестов).
Эта настройка хранится в файле .testrunconfig вашего проекта. Через UI ее можно выставить следующим образом:
  1. Откройте ваш файл .testrunconfig
  2. Выберите пункт Data and Diagnostics
  3. Выставите флажок напротив Code Coverage и нажмите Configure
  4. Выберите библиотеку, для которой необходимо собирать покрытие
  5. Выключите флажок Instrument assemblies in place

После сохранения файла внутри него будет следующий текст:

<?xml version="1.0" encoding="UTF-8"?>
<TestSettings name="Local Test Run" id="de0d45b4-4fed-4acb-a663-2cfdf0ce4fd7" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
 <Description>This is a default test run configuration for a local test run.</Description>
 <Deployment enabled="false" />
 <Execution>
  <Timeouts testTimeout="300000" />
  <TestTypeSpecific>
   <UnitTestRunConfig testTypeId="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b">
    <AssemblyResolution>
     <TestDirectory useLoadContext="true" />
    </AssemblyResolution>
   </UnitTestRunConfig>
  </TestTypeSpecific>
  <AgentRule name="LocalMachineDefaultRole">
   <DataCollectors>
    <DataCollector uri="datacollector://Microsoft/CodeCoverage/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TestTools.CodeCoverage.CoveragePlugIn, Microsoft.VisualStudio.QualityTools.Plugins.CodeCoverage, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="Code Coverage">
     <Configuration>
      <CodeCoverage xmlns="">
       <Regular>
        <CodeCoverageItem binaryFile="YourProject\bin\Debug\YourProject.dll" pdbFile="YourProject\bin\Debug\YourProject.pdb" />
       </Regular>
      </CodeCoverage>
     </Configuration>
    </DataCollector>
   </DataCollectors>
  </AgentRule>
 </Execution>
</TestSettings>

Секция DataCollectors как раз и говорит MSTest'у, что необходимо собирать Code Coverage.

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

Первый кейс нам не подходит, т.к. этот файл хранится в системе контроля версий, поэтому любой разработчик может выключить настройку по сбору Code Coverage и положить этот файл назад. Или наоборот, если мы, например, не хотим в некоторых случаях собирать эту метрику. По этим причинам настройка о том, собирать или не собирать метрику должна быть включена в CruiseControl. Второй вариант также не позволяет полностью предотвратить проблему описанную выше. По вышеописанным причинам мы у себя в компании остановились на третьем варианте. Описывать способ создания XML файла я не буду, там всё достаточно просто.

Когда конфиг готов можно запускать тесты, для этого в конфиг файл CruiseControl'а в секцию с тасками необходимо добавить следующий код:

<exec>
  <executable>C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe</executable>
  <buildArgs>"/testcontainer:PathToWorkingDirectory\Source\bin\Release\YourTestProject.dll" "/runconfig:PathToYourConfig\localtestrun.testrunconfig" "/resultsfile:PathToWorkingDirectory\TestResults.xml"</buildArgs>
  <description>Executing MSTest</description>
</exec>


Если запустить сейчас CruiseControl, то файл data.coverage будет создан в папке PathToWorkingDirectory\[USERNAME]_[MACHINE-NAME] [DATE AND TIME]\In\[MACHINE-NAME]. Т.к. в названии папки присутствует время, то в дальнейшем будет не очень удобно использовать ее. Поэтому в .testrunconfig необходимо добавить следующую секцию (внутрь <TestSettings>):

<NamingScheme baseName="FolderName" appendTimeStamp="false" useDefault="false" />


Теперь файл data.coverage можно будет найти в папке PathToWorkingDirectory\FolderName\In\[MACHINE-NAME].

После проделанных действий в папке с результатами тестов для этого проекта на машине с CruiseControl’ом будет находиться файл data.coverage. Проблема в том, что этот файл бинарный, поэтому нам необходимо сконвертировать его в xml.

Конвертация data.coverage в XML


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

using (CoverageInfo info = CoverageInfo.CreateFromFile(PathToDataCoverageFile))
{
     CoverageDS data = info.BuildDataSet();

     string outputXml = data.GetXml();
     File.WriteAllText(PathToOutputXmlCoverageFile,outputXml));
}


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

Запуск нашего консольного приложения осуществляется благодаря дополнительной секции в конфиге билд-сервера:

<exec>
  <executable>PathToConverter\Coverage2XmlConverter.exe</executable >
  <buildArgs>"PathToDataCoverage\data.coverage" "PathToOutputXml\coverage.xml"</buildArgs>
  <description>Calculation code coverage data</description>
</exec>


Добавление данных к результатам билда


Теперь у нас почти всё готово. Достаточно добавить получившийся XML файл к результатам билда. Делается это достаточно просто, необходимо добавить следующую секцию в конфиг CruiseControl'а внутрь секции Publishers:

<merge>
   <files>
     <file>PathToResults\TestResult.xml</file>
     <file>PathToCoverage\coverage.xml</file>
   </files>
</merge>


Файл TestResult.xml уже должен был быть у вас в конфиге, если у вас настроена работа с тестами.

Отображение данных на странице с билдом


Осталось написать файл XSLT для отображения собранных данных на страницу с билдом. На билдах, в которых покрытие составляет меньше 20%, показатель Code Coverage будет красным, в случае 20%-50% показатель будет желтым и в случае >50% он будет зеленым. Эти значения легко можно поменять в XSLT, и использовать те, которые больше вас устраивают.

Общее значение покрытия мы показываем на странице с билдом, вот как теперь это выглядит:

image

Для этого пришлось немного модифицировать MSTestReport2008.xsl из стандартного набора CruiseControl.NET.

А так выглядят детализация Code Coverage метрики:

image

Занимать место в статье текстом XSLT я не буду, поэтому выложил на GitHub 2 XSLT-файла, вместе с приложением для конвертации data.coverage файла в XML.

Спасибо большое, буду раз замечаниям и дополнениям.
Tags:.netcode coveragecruise controlcruisecontrolcsharp
Hubs: .NET
+22
3.2k 31
Comments 21