Unit testing is a common practice in software development which involves breaking pieces of code down into small "units" which can be individually tested in isolation against their expected behavior for given scenarios.

This post is intended to be a brief tutorial of how to create and integrate unit tests into your .net core projects however if you'd like to read more about what unit tests are and how they can benefit your codebase you can read more information here and a good discussion titled Is Unit Testing Worth The Effort.

Creating a Unit Test Project

The .Net CLI makes it easy to create a new unit test project with a choice of the xUnit or MsTest test frameworks, for this demo we shall be using xUnit.

Use the dotnet new command to create a new xunit test project and open it in VsCode.

dotnet new xunit --name MyTestProject  
cd MyTestProject  
code .  
Writing a first Unit Test

Our new project file should contain a class file called UnitTest1.cs where we will write our test. In a nutshell, a test must should compare the output of a function (or some lines of code) with a expected value using the Assert library that's included as part of the xUnit library.

In this example we'll assert that 2 x 5 is equal to 10.

using System;  
using Xunit;

namespace MyTestProject  
{
    public class UnitTest1
    {
        [Fact]
        public void Test1()
        {
            var expected = 10;
            var actual = 2 * 5;

            Assert.Equal(expected, actual);
        }
    }
}
Running Unit Tests with the CLI

To run unit tests the CLI contains a test command that will run our tests and give us some feedback.

dotnet test

[xUnit.net 00:00:02.1934262]   Discovering: MyTestProject
[xUnit.net 00:00:02.6135422]   Discovered:  MyTestProject
[xUnit.net 00:00:02.7951588]   Starting:    MyTestProject
[xUnit.net 00:00:03.3670330]   Finished:    MyTestProject

Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.  
Test Run Successful.  

Take note of the first execution step - Discovering Tests. Tests are generally identified as void methods in our test project that have been decorated with the Fact (or Theory) property.

If a test fails, the test runner will provide some feedback on where the test failed. In this example I've changed the expected variable to be 11 instead of 10 to make the test fail.

Failed   MyTestProject.UnitTest1.Test1  
Error Message:  
 Assert.Equal() Failure
Expected: 11  
Actual:   10  
Stack Trace:  
   at MyTestProject.UnitTest1.Test1() in /home/Source/MyTestProject/UnitTest1.cs:line 1
4  
Test Run Failed.

Total tests: 1. Passed: 0. Failed: 1. Skipped: 0.  
Installing additional test libraries

While the xUnit framework on its own provides plenty of tools to create a test suite, you may want to include additional packages to improve productivity and readability of your tests. In this instance we'll add the FluentAssertions library to our project to enhance the readability of our test.

Install the nuget package to our project we need to use the dotnet add package command.

dotnet add MyTestProject.csproj package FluentAssertions  

With the addition of this package, we should now be able to access the library's Should extension methods instead of using xUnit's Assert method.

using System;  
using Xunit;  
using FluentAssertions;

namespace MyTestProject  
{
    public class UnitTest1
    {
        [Fact]
        public void Test1()
        {
            var expected = 11;
            var actual = 2 * 5;

            actual.Should().Be(expected);
        }
    }
}
Debugging Unit Tests with VsCode

Debugging tests is now very easy with VsCode, just set a breakpoint and click the text "Debug Test" which should appear above the method name and VsCode should handle the rest.

Fig 1: Click debug test to launch the debugger
Fig 2: Debugging a test

Other command line features

If you're using a test suite consisting of multiple projects, you can use the list function to output a list of all the tests in a single project.

dotnet test MyTestProject.csproj -t

The following Tests are available:  
[xUnit.net 00:00:01.9573981]   Discovering: MyTestProject
[xUnit.net 00:00:02.3525320]   Discovered:  MyTestProject
    MyTestProject.UnitTest1.Test1
    MyTestProject.UnitTest1.Test2
    MyTestProject.UnitTest1.Test3

You can target a specific test/group of tests using the filter property.

// Finds all tests with the text Test3 in
dotnet test MyTestProject.csproj --filter DisplayName~Test3

// Run test 1 only
dotnet test MyTestProject.csproj --filter DisplayName=MyTestProject.UnitTest1.Test1

// Run all tests apart from test 1
dotnet test MyTestProject.csproj --filter DisplayName!=MyTestProject.UnitTest1.Test1
Adding a test project to an existing solution

We've covered a brief overview of how to create and run some tests but how can we integrate a unit test project with an existing code library? Here is a short example using the CLI.

// create solution file called NameGenerator
mkdir NameGenerator  
dotnet new sln

// create project for business logic
mkdir NameGenerator.Business  
cd NameGenerator.Business  
dotnet new classlib  
cd ..

// create project for tests
mkdir NameGenerator.Tests  
cd NameGenerator.Tests  
dotnet new xunit  
cd ..

dotnet sln add NameGenerator.Business/NameGenerator.Business.csproj  
dotnet sln add NameGenerator.Tests/NameGenerator.tests.csproj  
dotnet add package FluentAssertions

// Add a reference to our Business Logic code to our test project
dotnet add reference ../NameGenerator.Business/NameGenerator.Business.csproj

code .  

We can add some simple code to our Business Logic project.

using System;

namespace NameGenerator.Business  
{
    public class NameGenerator
    {
        public string GenerateName(string forename, string surname)
        {
            return $"{forename} {surname}";
        }
    }
}

We should then add the following code to our test project to verify our functionality works as expected.

using System;  
using Xunit;

using FluentAssertions;  
using NameGenerator.Business;

namespace NameGenerator.Tests  
{
    public class NameGeneratorTests
    {
        [Fact]
        public void Test1()
        {
            var sut = new NameGenerator.Business.NameGenerator();
            var expected = "Joe Bloggs";

            sut.GenerateName("Joe", "Bloggs")
                .Should()
                .Be(expected);
        }
    }
}

Now we should be able to run our test against the business logic code with dotnet test.

I hope that through these examples you can get up and running with writing and integrating unit tests into your .Net Core projects using the CLI.