.Net Core CLI

2 posts

Introduction to Unit Testing on .Net Core with xUnit and the CLI

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.

Using the .Net Core CLI to Organise Projects and Solutions

When starting out building applications with .Net Core, it's very easy to spin up a new project using "dotnet new" and start writing code however when it comes to creating more complex solutions spanning multiple projects.

Unfortunately Visual Studio Code and other cross platform editors don't provide as much help with this as the full version of Visual Studio on PC. Thankfully the .Net Core SDK comes loaded with a command line interface (CLI) that provides functionality for most of the common actions which are.

Create new solution
// uses current dir name
dotnet new sln

// specify a solution name
dotnet new sln --name mysolution  
Create new project
dotnet new [type]  
dotnet new [type] --name myproject

project types: web, mvc, webapi, classlib, mstest, xunit, webconfig  
Manage projects in solution
dotnet sln mysolution.sln add path/myproject.csproj  
dotnet sln mysolution.sln add path1/proj1.csproj path2/proj2.csproj

dotnet sln mysolution.sln remove path/myproject.csproj  
dotnet sln mysolution.sln remove path1/proj1.csproj path2/proj2.csproj

// shows all projects in solution
dotnet sln mysolution.sln list  
Manage nuget packages in a project
dotnet add package packagename  
dotnet add myproj.csproj package packagname

dotnet remove package packagename  
dotnet remove myproj.csproj package packagename  
Manage references in a project
dotnet add reference path/myproject.csproj  
dotnet add myproject.csproj reference path/myref.csproj

dotnet remove reference path/myproject.csproj  
dotnet remove myproject.csproj reference path/myref.csproj

// list all references in csproj
dotnet list reference  
dotnet list myproject.csproj reference  
Build & run solutions
// commands
dotnet restore  
dotnet clean  
dotnet build  
dotnet run

// target specific project
dotnet [command] -p myproject/myproject.csproj

// build in release mode
dotnet [command] -c release  
Deploy a solution
dotnet pack  
dotnet pack path/myproj.csproj

//use release mode
dotnet pack -c release

// outputs myproj.1.0.0-Preview.nupkg
dotnet pack path/myproj.csproj --version-suffix Preview

dotnet publish  
dotnet publish -p myproj.csproj  
dotnet publish -c release  
Adding new classes/files

The .Net Core CLI doesn't provide functionality to create new class files yet however Omnisharp's C# extension for Visual Studio Code enables you to right click and create these files.

Hopefully some of you can find some value in this .Net CLI crash course and that it helps you familiarise yourself with the CLI in a short space of time.