Click here to Skip to main content
14,643,840 members
Articles » Development Lifecycle » Code Generation » General
Posted 8 Nov 2018


9 bookmarked

Code Coverage is Finally Easy in .NET Core

Rate this:
4.95 (10 votes)
Please Sign up or sign in to vote.
4.95 (10 votes)
8 Nov 2018CPOL
Code coverage is finally easy in .NET Core

A couple of months ago, calculating code coverage on the command line was quite challenging in ASP.NET Core. Fortunately, as of last month and Visual Studio 15.8, generating the metric is easy.

Originally, this was the story of the pain involved me starting a new project recently based on Microsoft's new, modern, cross-platform tech stack. I was going to explain the many steps I went through to calculate what seems like a fairly basic metric.

But instead, I'm going to tell you how much happier your life can be.

Background Story

The tech stack on my new project looks like this:

  • ASP.NET Core
  • XUnit
  • Azure DevOps (VSTS)
  • Cake

But wait, before I alienate my audience down to zero readers, let me back up a little, bear with me.

If you read my last blog post on the importance of calculating code coverage from a team perspective, you know what code coverage is and why I care so much. And if you've read my previous articles (or videos) about why Cake is such a fantastic tool for automating a devops pipeline, then you know how I propose to approach the devops problem. If not, let me tl;dr the Cake decision:

  • Vendor Neutral (could easily switch to Jenkins or Team City)
  • Task Dependency Management Support
  • Locally Runnable
  • Easily Debuggable
  • Version Controlled
  • C#

'nuf said. The rest of the technologies were project requirements, except XUnit, which came with ASP.NET Boilerplate (more on that in a future article), and I didn't care enough to change it. Just replace "X" with "N" in the rest of this article if you're an NUnit fan, it'll be the same.

Testing with ASP.NET Core

I'm going to pick up this narrative after having installed cake and scripted the build task, something like this:

    .Does(() =>
    var settings = new DotNetCoreBuildSettings { 
        Configuration = configuration 
    DotNetCoreBuild("./CakeCoverageTest.sln", settings);

At that point, I was extremely happy to discover that because testing is a first-class citizen I could test like this:

    .Description("Runs unit tests.")
    .Does(() =>
    var testLocation = File("./CakeCoverageTest.Test/CakeCoverageTest.Test.csproj");
    var settings = new DotNetCoreTestSettings {
        NoBuild = true
    DotNetCoreTest(testLocation, settings);

Which translates to:

dotnet.exe test "CakeCoverageTest.Test/CakeCoverageTest.Test.csproj" --no-build

It runs ASP.NET Core's built-in testing infrastructure, which natively recognizes XUnit and NUnit.

The NoBuild = true is because otherwise the .NET test command will try to build, and I like to let Cake handle my dependencies. This prevents building multiple times if, for example a high level Deploy task is dependent on both Test and Package, and both of those are dependent on Build.


Image 1

In that case, Cake is smart enough to only build once if I run Deploy. This isn't really any different than any "AKE" tool like Make, Psake, Rake, or MS Build, but it's a nice benefit over bash or Powershell scripts where you'd have to use global state. Task dependency management++.

Getting Coverage

Before Visual Studio 15.8, getting coverage would have involved switching to the windows-only vstest.console.exe alternative and requiring Visual Studio Enterprise. Then, it would require adding a test adapter, fixing a bug with the tool path, adding a referencing the Microsoft.CodeCoverage NuGet package, adding a <debugtype>Full line to the main .csproj, and me explaining about the new vs the old pdb (Program DataBase) debug information file.

Fortunately, as of September, there is a new parameter to .NET test: --collect "Code Coverage". It's unfortunately still Windows-only, but they have removed the requirement for Visual Studio Enterprise. Making it cross platform is on the radar, and may even be supported by the time you read this.

Cake doesn't support the coverage arguments just yet, but with the flexibility of the ArgumentCustomization parameter, there's a simple workaround:

    .Description("Runs unit tests.")
    .Does(() => 
    var testLocation = File("./CakeCoverageTest.Test/CakeCoverageTest.Test.csproj");
    var settings = new DotNetCoreTestSettings {
        Configuration = configuration,
        NoBuild = true,
        ArgumentCustomization = args => args
            .Append("--collect").AppendQuoted("Code Coverage")
    DotNetCoreTest(testLocation, settings);

That translates to:

dotnet test "CakeCoverageTest.Test/CakeCoverageTest.Test.csproj" 
--configuration Release --no-build --collect "Code Coverage" --logger trx

And with a little luck, it should output something like this:

Starting test execution, please wait...
Results File: 
 Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
Test Run Successful.

How awesome is that?! We're pretty much done.

Publishing Coverage

To view the coverage data inside of Visual Studio, we sadly still need the Enterprise edition. But regardless, we can use an Azure DevOps build definition task to pick up and publish the file. First, the build task:

Image 2

There's a Cake task in the marketplace, but the built in Powershell task above works just fine too.

Then all we need is to publish the test results:

Image 3

If we run that puppy, we should get this:

Image 4

Check out that line "Code coverage succeeded" with the "50.00% lines covered" row right up front! Like butter.


I originally set out to tell a story of woe, and pain, and gnashing of teeth. Instead I'm happy to tell you what a wonderful world we now live in. Calculating code coverage is now easy for ASP.NET Core. Well done, Microsoft. Well done!


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Lee P Richardson
Web Developer
United States United States
Lee is a prolific writer, speaker, and video producer on .Net and open source topics. He has published over 100 posts to his personal blog ( that have received more than half a million views since 2007. His "Code Hour" YouTube channel ( has attracted nearly 1,000 subscribers who have collectively consumed over 5,900 hours of his content. StackOverflow ranks him as a top 2% contributor. He has published 25 articles to CodeProject with an average article rating of 4.96/5. Throughout his 20 year software development consulting career in the DC area he has spoken scores of times at code camps, conferences, and user groups. He created the Siren of Shame (, and is a Solution Samurai at InfernoRed ( He is active on twitter where you can reach him @lprichar (

Comments and Discussions

SuggestionUseful tip Pin
Bogdan Marian13-Nov-18 9:44
professionalBogdan Marian13-Nov-18 9:44 
GeneralRe: Useful tip Pin
Lee P Richardson11-Dec-18 12:31
MemberLee P Richardson11-Dec-18 12:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.