Simple PowerShell script for producing code coverage reports in .NET Core

Use PowerShell to glue Coverlet results with ReportGenerator when dealing with randomly generated output paths.

Over the past two decades its become increasingly easy to test your code and determine which lines of code have been covered by your tests.  If your developing with .NET Core this is never been easier.  By using Coverlet, ReportGenerator, and a little bit of PowerShell, you can have a test coverage report within minutes.

I’ll admit there are plenty of blog posts and documentation out there that describe how to uses these tools.  But I want to tackle a specific problem that I’ve come across.  And from reviewing GitHub and StackOverflow, there are a few of you out there experiencing this same problem.  When you run dotnet test --collect="XPlat Code Coverage" the Coverlet tool will report the output paths of the cobertura xml files containing the coverage results.  But each time the tests are run, the output files are placed in a new randomly generated sub-directory with a guid string as the name.  This can pose a small challenge when setting up automation.

Here’s an example output of dotnet test:

Starting test execution, please wait...
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
A total of 1 test files matched the specified pattern.

Attachments:
  C:\Users\jbruun\source\repos\ClassLibrary1\TestProject2\TestResults\3aa87678-eabd-4c15-b744-af4bf3d79eac\coverage.cobertura.xml
Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, Duration: 2 ms - TestProject2.dll (net5.0)

Attachments:
  C:\Users\jbruun\source\repos\ClassLibrary1\TestProject1\TestResults\b68921b8-ff74-46f3-b809-4be614952a31\coverage.cobertura.xml
Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, Duration: 2 ms - TestProject1.dll (net5.0)

Notice the output paths on lines 7 and 11.

We need to extract those two pathes, and concatenate them with a semi-colon separator so it can be fed to report generator.  This can be done with a little bit of PowerShell.

#Requires -Version 7.0
$TestOutput = dotnet test --test "XPlat Code Coverage"
$CoverageReports = $TestOutput | Select-String coverage.cobertura.xml | ForEach-Object { $_.Line.Trim() } | Join-String -Separator ';'
dotnet reportgenerator "-reports:$CoverageReports" "-targetdir:./CoverageReport" "-reporttype:Html"

It uses Select-String to match the lines of output text containing coverage.cobertura.xml which returns a list of MatchInfo objects.  These MatchInfo objects are piped through ForEach-Object to extract the line text and trims the excess whitespace.  These strings are joined using Join-String with a semi-colon separator and stored in the $CoverageReports variable that is fed to reportgenerator.

Note: The Join-String cmdlet requires PowerShell 7.0 or later.

I hope this helps a few of you out there with your automation efforts.  Cheers!