.NET Core Code Coverage
Tags: codecoverage, dotnet
Published: 2021-03-07 09:34:13

Originally published at https://eduardstefanescu.dev/2020/02/22/dotnet-core-code-coverage/.

---

Code coverage tools are a great way to improve the code quality, but there are a lot of tools that require a paid license to use it like dotCover, the Enterprise version of Visual Studio or Visual Studio extensions. After some research, I found out that two free tools combined can generate code coverage reports. The first tool is called [Coverlet](https://github.com/tonerdo/coverlet) which generates the code coverage as I wanted and it's also working with .NET Framework. Essentially is creating an `XML` report file that covers the lines, branches, and methods. And the second tool is [ReportGenerator](https://github.com/danielpalme/ReportGenerator) which is used for parsing the generated `XML` and expose the data in a friendly format.

For this article, I created a small project to demonstrate how is working.
Let's begin with the Business layer; which contains two classes, a dummy `User` that only has two properties `Roles` and `ManagedCountries`.
```
public class User
{
    public IEnumerable<string> Roles { get; set; }
    public IEnumerable<string> ManagedCountries { get; set; }
}
```
And a `UserService` that checks if the `User` has sufficient rights to delete a country.

I know that the return of the `CanDeleteCountry` method can be simplified, but I will keep it as it is, just for the sake of this example.
```
public class UserService
{
  private readonly User user;

  public UserService(User user)
  {
    this.user = user;
  }

  public bool CanDeleteCountry(string country)
  {
    bool isCountryManager = user.Roles.Any(u => u == "CountryManager");
    bool canManageCountry = user.ManagedCountries.Any(c => c == country);

    if (isCountryManager && canManageCountry)
    {
      return true;
    }

    return false;
  }
}
```
The Tests project uses `NUnit`, but I saw that `Coverlet` and `ReportGenerator` also work with `MSBuild` and `xUnit`, for those you only need another TestAdapter and TestLogger. The packages installed for this project are:
- [NUnit](https://www.nuget.org/packages/NUnit)
- [NUnit3TestAdapter](https://www.nuget.org/packages/NUnit3TestAdapter)
- [NunitXml.TestLogger](https://www.nuget.org/packages/NunitXml.TestLogger)
- [Microsoft.NET.Test.Sdk](https://www.nuget.org/packages/Microsoft.NET.Test.Sdk)
- [Microsoft.CodeCoverage](https://www.nuget.org/packages/Microsoft.CodeCoverage)
- [coverlet.msbuild](https://www.nuget.org/packages/coverlet.msbuild)
- [ReportGenerator](https://www.nuget.org/packages/ReportGenerator)

In order to get the code coverage, a script is needed that will run the tests, create the `XML` file report and generate an `HTML` file with the reports.
```
dotnet test --logger "trx;LogFileName=TestResults.trx" ^
            --logger "nunit;LogFileName=TestResults.xml" ^
            --results-directory ./Coverage ^
            /p:CollectCoverage=true ^
            /p:CoverletOutput=Coverage\ ^
            /p:CoverletOutputFormat=cobertura ^
            /p:Exclude="[nunit.*]*

dotnet %userprofile%\.nuget\packages\reportgenerator\4.4.7\tools\netcoreapp3.0\ReportGenerator.dll ^
	"-reports:Coverage\coverage.cobertura.xml" ^
	"-targetdir:Coverage" ^
	-reporttypes:HTML

start .\Coverage\index.htm\
```
I've created a batch file to be easier to run all the commands in a single unit. Also please note that if you are using Mac or Linux, the `nuget` packages path, would be: `~/.nuget/packages`.
The first command will run the tests and will generate the `XML` file used by the second command to generate the report. And the third command will open `index.html`.
Please consider adding the `Coverage` folder into `.gitignore` if you are working on a team, this is just for local purposes and it would be nice if the project you are working on has on the CI build process a step where the code coverage is generated.

So now, with all that in place, let's see the results. I wanted to test only if the `User` is not a `CountryManager` and skip the other conditions that will also check for the managed countries because I wanted to generate a report with all the available covered areas.
```
[Test]
public void CanDeleteCountry_UserIsNotCountryManager_ReturnsFalse()
{
  var user = new User
  {
    Roles = new[] { "User" },
    ManagedCountries = new[] { "Sweden" }
  };
  var userService = new UserService(user);

  bool canDeleteCountry = userService.CanDeleteCountry("Romania");

  Assert.IsFalse(canDeleteCountry);
}
```
Let's take a look at the code coverage report. When the `index.html` file is open, the first page contains an overview of the ran tests, it offers a brief summary of the report and a grouping feature, that can be used to view the files on different layers (i.e. No grouping, By assembly, By namespace, Level: 1 and By namespace, Level: 2).
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tixnscz1g197t20xa9hp.png) 
The test report for the `UserService` class looks something like this:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/he9uhjse94p9gu7j300y.png) 
The color code is the same as in the other tools, but what I like is that it also has branch coverage, which in the above picture is the yellow area.
In conclusion, these two tools are great and don't cost anything, also can be configured to run through all test projects and generate more comprehensive code coverage.
Here is the repository link with all the code from this article: https://github.com/StefanescuEduard/NetCoreCodeCoverage. I kept the Coverage folder on the repository just for testing purposes.
Thanks for reading this article, if you find it interesting please share it with your colleagues and friends. Or if you find something that can be improved please let me know.