How to include code coverage in Azure DevOps pipeline?
Tags: codecoverage, dotnet, tutorial, devops
Published: 2021-01-16 19:20:32

By code coverage we mean the action of trying to measure how much of our code has been executed by our tests.
This sound like 

> __TL;DR__ 
>
> Untested code is a broken code.
> Definitely a strong statement but true in a way, we don't always manage to get enough coverage.
> Often this happens because we don't have time, other times because despite having written tests we are not able to read the metrics. 

So, how we can "humanize" code coverage metrics? And how we can generate its?

To answer at these questions I usually use two libraries.

{% github coverlet-coverage/coverlet no-readme %}

to gather metrics, and

{% github danielpalme/ReportGenerator no-readme %}

for generate human-readable reports.

## How can set-up __*coverlet*__?

I usually include [coverlet.msbuild](https://www.nuget.org/packages/coverlet.msbuild/) by [MSBuild .targets Files - Visual Studio | Microsoft Docs](https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-dot-targets-files).

{% gist https://gist.github.com/binick/71ec60d143ee51523e2c7ac23b9ae543 file=CollectCoverage.targets %}

For alternative ways to include _coverlet_ into yout test project see also [coverlet-coverage/coverlet: Cross platform code coverage for .NET (github.com)](https://github.com/coverlet-coverage/coverlet#Quick-Start).

## How can set-up __*ReportGenerator*__?

In keeping with above to include [ReportGenerator](https://www.nuget.org/packages/ReportGenerator) by [MSBuild .targets Files - Visual Studio | Microsoft Docs](https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-dot-targets-files).

{% gist https://gist.github.com/binick/71ec60d143ee51523e2c7ac23b9ae543 file=ReportGenerator.targets %}

Also this tool offer a various way to use it, you can find all ways onto official documentation [ReportGenerator - converts coverage reports generated by coverlet](https://danielpalme.github.io/ReportGenerator/).

## How to wire-up all that?

To make everything work we need to add another _MSBuild_ file.

{% gist https://gist.github.com/binick/71ec60d143ee51523e2c7ac23b9ae543 file=Test.targets %}

And include this into your test project, something like this

```xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <Import Project="Tests.targets" />
  
</Project>
```

Now everything you are able to run `dotnet test` you will able to inspect and analyze something like this

![coverage-report)](https://camo.githubusercontent.com/adbd81e4a37adb003d75ecc13d508acda0c7aebc/68747470733a2f2f64616e69656c70616c6d652e6769746875622e696f2f5265706f727447656e657261746f722f7265736f75726365732f73637265656e73686f74312e706e67)

![coverage-report-detail](https://camo.githubusercontent.com/05adffdf7d564ad20b92b174c47353a26337ecdc58887d0c64a3b4e633e8770a/68747470733a2f2f64616e69656c70616c6d652e6769746875622e696f2f5265706f727447656e657261746f722f7265736f75726365732f73637265656e73686f74322e706e67)

I think that is an amazing tool to understand at a glance which codes are covered and which not.

## And now, how I can put it into Azure DevOps pipeline?

It would be nice if this report came was published into the _Build pipeline_ report, don't you think? Maybe even include branch policies for it.

Well that's possible by use [Publish Code Coverage Results task](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/test/publish-code-coverage-results), something like this:

```yaml
- task: PublishCodeCoverageResults@1
  displayName: Publish Code Coverage Results
  inputs:
    codeCoverageTool: 'cobertura'
    summaryFileLocation: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)/Reports/Summary/Cobertura.xml'
    continueOnError: true
    condition: always()
```

We notice the `summaryFileLocation` argument, this means that we will push only one file to _Azure DevOps_ why?

One unwrite note of _Publish Code Coverage Results task_ or limitation, I don't know, is that the sum of covered lines, when we publish more reports, is take from the first file ![](/contents/pipeline-run-summary.png)

This results in an unreliable result.

To fix that problem we can marge multiple reports into a summary reports so that can be publish it only one. One way to make it is the follow

{% gist https://gist.github.com/binick/71ec60d143ee51523e2c7ac23b9ae543 file=SummaryReportGenerator.proj %}

and run _MSBuild_ project into the pipeline with

```yaml
- script: dotnet msbuild SummaryReportGenerator.proj /p:Configuration=$(Configuration)
  name: GenerateCodeCoverageSummary
  displayName: Generate code coverage summary
```

Once you've done this the sum of covered lines on _Build pipeline_ will true.