Automated Unit Testing With NUnit And Coverlet on Azure DevOps

Automated testing is crucial in the overall CI/CD pipeline, not because it’s a fancy word or because it sounds very cool to say among your colleagues, rather its because it prevents code-panics, allows for faster feature deployments with minimal issues, ensures that new features implemented are well tested, and that the mater branch remains as holy as possible.

As your test suite grows it becomes quite cumbersome to have to run the test suite every single time you implement a new feature or check in a code, wouldn’t it be nice if we could automate the entire test run process for every code check-in ?

This brings us to automating our tests, Test Automation/Automated Unit Testing forms an integral part of the CI/CD pipeline (although this opinion is debatable), in this tutorial we would see how to achieve test automation using NUnit as our testing framework, Azure Devops as our platform and using Coverlet as our coverage tool. This article would not give a breakdown of how to write unit tests in MVC Core, there are plethora of articles online that does sufficient justice to the subject. The repo used for this article can be found here.

Having said that, what’s our game plan? Well these are the steps we would take

  1. Review project dependencies
  2. Import Project from GitHub into Azure DevOps organization
  3. Queue build for run

Let’s review the main application and see key components that are integral to a successful unit test automation. Firstly, all unit test projects have references to NUnit, coverlet.msbuild, NUnit3TestAdapter, Microsoft.NET.Test.Sdk this four different packages are the essentials for unit testing and code coverage.

CSproj snippet for test project
YAML Snippet

Secondly, the image above contains information about our pipeline build configuration. Let’s walk through it together

1. The trigger refers to what branch continuous integration is setup for. This means that for every push/PR to the master, run this build. As a side note, DO NOT COMMIT TO MASTER. Rather submit a PR to master, you can learn more here

2. Pool refers to the agent responsible for the pipeline build. We have configured it to use a vmImage of Ubuntu. What this means is that we need a machine in the cloud to run our build and the different scripts and commands as we deem necessary for our project, we could of course define our own custom machine, rather we have chosen to use a Virtual Machine using Ubuntu image

3. What we have defined here is basically that we are building for Release, as compared to working with a Debug form.

4. This contains the major part of our build process. Firstly, we are defining script command to be dotnet build with the configuration flag, also we have indicated with the displayName action to output to Azure DevOps when we get to this section.

After the displayName, we run our unit tests using the DotNetCLI, at the Projects flag we specify the directory containing the Unit tests, we use the path UnitTests/*.Tests/*.csproj the * symbol indicates a wildcard meaning every project in the UnitTests directory that ends with “.Tests” contains unit tests. At the arguments section we specify additional commands for running our unit tests, we have specified we want to collect code coverage and the output format for the code coverage to be Cobetura this ties in to the Coverlet package we referenced in our project CSProj.

5. This is the last task of the build. Here we output the code coverage from the test run, we specify the code coverage tool to use, and also specify the location of the file generated from Coverlet from which to get the code coverage information from.

To continue with this article you would need to fork the project repo into your github account or you could work with your own project and just adjust the file path for the unit tests in the YAML file.

Navigate to Azure DevOps create a unique organization name, then create a project within the organization you created earlier, leave the selection as default. The screenshot below might be different by the time you are reading this, but the process is still the same

Create a New Organization
Create a Project in the Organization

After creating the project you are redirected to the project dashboard. From the dashboard navigate to Repos->Files. Of course, there wouldn’t be any file in the system, we would rather import our repository from Github by selecting the import a repository. Specify the web URL of your forked repo and supply your Github credentials to allow access for bringing the repo over from Github into Azure DevOps.

Empty Azure Repo File
Importing Repo from Github

After importing from Github, we are shown all files now with the commit and other information. Navigate to Pipelines->Builds, you are presented with a page indicating no build pipeline configured. Click on new pipeline, specify Azure Repos Git as the location of the repo, select the imported repo as the main repo. At this point Azure DevOps either acknowledges the presence of the YAML file in the repo or you have to specify the path to the file itself, then click the run button to start the process.

Build File On Azure DevOps

After the build process is completed you should see a Code Coverage tab that outlines the code coverage for the entire project.

Code Coverage results

By looking at the code coverage of the project we can see parts of the codebase that are well tested and other parts that aren’t. If working in a collaborative team we can extend the automated unit testing workflow by ensuring that every PR has a successful build from this pipeline and also enforce branch policies on the master so this ensures no team member commit to the master.

C# | Azure Advocate | Azure DevOps Fanboy | Naruto Guy | Diehard DC Comics fan