Сегодня я читал сообщение Скотта Хансельмана о покрытии кода в проектах .Net Core. Внезапно я начал копаться в сообщениях об инструментах, о которых я никогда не слышал, но это звучало довольно интересно, таких как Coverlet и ReportGenerator. Тогда я подумал, что они могут вписаться в наши потоки CI.

Признаюсь, когда я начинал разработку .NET, я вообще не занимался модульным тестированием. Однако, благодаря таким хорошим людям, как Луис Фрайле и Унаи Зоррилла Кастро, я вскоре понял, что они настолько важны, что я не смогу жить без них. Итак, я принял Microsoft's MsTest и начал писать множество [TestClass] и [TestMethod]. Потом в какой-то момент я услышал о xUnit, и снова все изменилось. Я забыл об атрибутах [TestClass] и начал писать [Fact] s и [Theory] es.

Я использую их в течение долгого времени, но проблема, которую я обнаружил при переходе с MsTest на xUnit, заключалась в том, что информация, отображаемая в результатах сборки Visual Studio Team Services, была очень плохой, чтобы быть вежливой. И данных о покрытии кода вообще не было. Это действительно было плохо, но я ленив и отложил задачу по улучшению этого до сегодняшнего дня.

Покрывало

Первое, что я сделал, - это частично реализовал изменения, предложенные Скоттом Хансельманом, чтобы включить покрытие кода в мой проект. Coverlet - это пакет, который можно включить в ваши проекты и генерировать информацию о покрытии кода во время сборки. В проекте говорится, что это буквально «кроссплатформенная библиотека покрытия кода для .NET Core с поддержкой покрытия строк, ветвей и методов».

Чтобы включить цели сборки Coverlet в свой проект, вы должны добавить следующую строку в файл csproj вашего тестового проекта:

<PackageReference Include="coverlet.msbuild" Version="2.0.1" />

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

dotnet test /p:CollectCoverage=true

По умолчанию это создает файл покрытия кода в формате JSON. Если вы хотите поэкспериментировать с расширением Visual Studio Code Coverage Gutters, как это было предложено Хансельманом, вы можете использовать такую ​​задачу в своем файле .vscode / tasks.json:

{
 "label": "test with coverage",
 "command": "dotnet",
 "type": "process",
 "args": [
  "test",
  "/p:CollectCoverage=true",
  "/p:CoverletOutputFormat=lcov",
  "/p:CoverletOutput=./lcov",
  "${workspaceFolder}/test/WebUserManager.Test/WebUserManagerTests.csproj"
 ],
 "problemMatcher": "$msCompile",
 "group": {
  "kind": "test",
  "isDefault": true
 }
}

Файл lcov.info можно проанализировать с помощью расширения Coverage Gutters и показать, какие строки охватываются вашими тестами. Это может быть очень удобно при локальной разработке, но для CI в VSTS нам потребуется использовать другой формат файла, понятный нашему инструменту создания отчетов. Это формат, используемый Cobertura, утилитой покрытия кода для Java, и его можно сгенерировать, вызвав dotnet test со следующими параметрами:

dotnet test 
  /p:CollectCoverage=true 
  /p:CoverletOutputFormat=cobertura

Будет создан файл с именем extension.cobertura.xml. Только это можно использовать для публикации данных о покрытии кода в результатах сборки VSTS. Однако мы сделаем еще один шаг и создадим полный отчет, который можно будет просматривать и просматривать со ссылкой на исходный код.

ReportGenerator

Инструмент, который нам понадобится, называется ReportGenerator, и его основное применение - создание красивых отчетов в нескольких форматах из файлов покрытия кода в других форматах. В нашем случае мы собираемся сгенерировать HTML-файл из нашего файла extension.cobertura.xml.

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

<ItemGroup>
  <DotNetCliToolReference Include="dotnet-reportgenerator-cli" Version="4.0.0-alpha12" />
</ItemGroup>

ПРИМЕЧАНИЕ: это альфа-версия инструмента, поэтому будьте готовы столкнуться с потенциальными проблемами и изменениями без уведомления. Если вам нужно что-то более стабильное, вы можете загрузить и установить ReportGenerator.exe в папку ваших агентов сборки и соответствующим образом установить PATH.

Теперь ваш файл тестера .csproj должен выглядеть примерно так:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <IsPackable>False</IsPackable>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\src\App\Acme.App.csproj" />
    <ProjectReference Include="..\..\src\Domain\Acme.Domain.csproj" />
    <ProjectReference Include="..\..\src\Infrastructure\Acme.Infrastructure.csproj" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
    <PackageReference Include="xunit" Version="2.3.1" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
    <PackageReference Include="Moq" Version="4.8.2" />
    <PackageReference Include="coverlet.msbuild" Version="2.0.1" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="dotnet-reportgenerator-cli" Version="4.0.0-alpha12" />
  </ItemGroup>
  <ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">
    <PackageReference Include="Microsoft.CodeCoverage" Version="1.0.3" />
  </ItemGroup>
</Project>

Настроить определение сборки

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

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

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

Тестирование и сбор покрытия

Это задача .NET Core со следующими параметрами:

  • Команда: test
  • Путь к проекту (ам): выберите только один тестовый проект. Должно быть возможно выбрать более одного с шариками, но я не пробовал.
  • Аргументы:
/p:CollectCoverage=true 
/p:CoverletOutputFormat=cobertura 
/p:CoverletOutput=$(Build.SourcesDirectory)\TestResults\Coverage\

Обратите внимание, как мы устанавливаем формат вывода на cobertura и папку, в которой вывод будет оставлен в подпапке с именем TestResults \ Coverage.

Генератор отчетов

Вторая задача также является задачей .NET Core со следующими параметрами:

  • Команда: custom
  • Путь к проекту (ам): такой же, как у первой задачи
  • Пользовательская команда: генератор отчетов
  • Рабочий каталог: установите в папку тестового проекта.
  • Аргументы:
"-reports:$(Build.SourcesDirectory)\TestResults\Coverage\coverage.cobertura.xml" "-targetdir:$(Build.SourcesDirectory)\TestResults\Coverage\Reports" -tag:$(Build.BuildNumber) -reportTypes:htmlInline

Здесь мы вызываем инструмент командной строки .Net Core ReportGenerator.exe, как объяснялось ранее, устанавливая для входного файла значение extension.cobertura.xml, формат вывода - Встроенный HTML (без внешних файлов js и изображений) и выходная папка в TestResults \ Coverage \ Reports.

ПРИМЕЧАНИЕ: обратите внимание на кавычки вокруг некоторых параметров, особенно тех, которые содержат пути.

На данный момент мы создали файл покрытия и отчет в следующих местах:

  • $ (Build.SourcesDirectory) \ TestResults \ Coverage \ охват.cobertura.xml
  • $ (Build.SourcesDirectory) \ TestResults \ Coverage \ Report \ *. htm

Опубликовать данные о покрытии

Последняя задача просто публикует сгенерированные данные. Добавьте задачу «Опубликовать результаты покрытия кода» со следующими значениями:

  • Инструмент покрытия кода: Cobertura
  • Сводный файл: $ (Build.SourcesDirectory) \ TestResults \ Coverage \ ** \ охват.cobertura.xml
  • Каталог отчета: $ (Build.SourcesDirectory) \ TestResults \ Coverage \ Reports

Вот и все. Окончательные результаты будут такими:

Больше информации: