Unit Tests in .NET Core: An Introduction

Jamie Taylor6 comments
Unit test introduction header imageSource: https://stocksnap.io/photo/BO5URB867F Copyright 2016 - Andrew Neel

Today’s header image was created by Andrew Neel, the original source for the image is available here

Now that you’ve built your first .NET Core application, it’s time to start thinking about unit tests.

What Is Unit Testing?

Unit Tests are small blocks of code that are run outside of your source code; their main purpose is to make assertions about and test the processes and algorithms used in your application’s source code.

Unit Tests are also a main component of TDD (Test Driven Development). In TDD, tests are written before any code is (which means that the test will initially fail), then the source code is built which will pass the test.

How Is Unit Testing Done In .NET Core?

Unit Tests are provided via a test library, and the test library runs against the rest of the source code.

A quick note before we begin actually building the tests: The source code used in this blog post can be found here

To show off how unit tests are created and run in .NET Core, we’re going to build an application that takes two numbers, and returns the sum of them.

It’s a pretty simple example, but it’s best to start small I guess.

Here’s the folder layout that we’re going to create:

Test-Library directory layout
The directory layout of our test-library

It’s a pretty simple layout:

  • All of our source code will be located within the src directory
  • The application that we’re going to build will be located within the app directory
  • The library that we’re going to build will be located within the library directory
  • The test library code will be located within the test-library directory (which is within the test directory)

App and Library

The Application and Library code are relatively simple. Here’s a break down of the Library:

  • In the Library, there is a single class called Thing.
  • Within the Thing class there is a single method called Get
  • Get has the following algorithm:
    • Take two integers and sum them
    • Store the result in a string (using String Interpolation)
    • Using JSON.NET deserialise the string back to an integer
    • Return the integer

And here is the code:

using static Newtonsoft.Json.JsonConvert;
namespace Library
{
public class Thing
{
// Takes two ints, adds them, stores them in
// a string (using string interpolation),
// then deserialises (via Json.Convert) back
// to an int and returns that value
public int Get(int left, int right) =>
DeserializeObject<int>($"{left + right}");
}
}
view raw thing.cs hosted with ❤ by GitHub

Super simple, right? On to Application!

  • Application creates a new instance of the Thing class
  • It then calls the Get method – passing 19 and 23 to it
  • The result of the Get method are then sent the the Console

And here is the code:

using static System.Console;
using Library;
namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
// Basic use of the Thing class Get method
var thing = new Thing();
WriteLine($"The answer is {thing.Get(19, 23)}");
}
}
}
view raw program.cs hosted with ❤ by GitHub

Again, this is pretty simple.

Test-Library

Aside from the project.json (which I’ve ignored for App and Library, but you can read at the GitHub link) Test-Library contains a single class called LibraryTests. And here it is:

using Library;
using Xunit;
namespace TestApp
{
public class LibraryTests
{
// A stupid test class which assets that 19 + 23
// is 42, via the Thing class' Get method
// Note: The Fact attribute tells the xunit
// that TestThing is a single test.
[Fact]
public void TestThing()
{
Assert.Equal(42, new Thing().Get(19, 23));
}
}
}
view raw tests.cs hosted with ❤ by GitHub

Here’s what it does:

  • TestThing creates an instance of the Thing class
  • TestThing calls the Get method of the Thing class and passes it two integers – 19 and 23
  • TestThing then Asserts that the returned value is equal to 42

How Do We Add a Unit Test?

And now we’re going to recreate the test-library class.

The first thing you’ll want to do is move to the test-library directory (creating it and it’s parent directory, if it doesn’t exist yet).

One you’ve done that, you need to tell .NET Core to create a new test library. So from the terminal enter the following:

dotnet new -t xunittest
view raw bash.sh hosted with ❤ by GitHub

This tells .NET Core to create a new class of the type (provided by the -t switch) xunittest.

You can read more about xunit and how to write tests using it over at the xunit GitHub page

This will create a project.json file in the test-library folder, but it will be incomplete. Here’s the incomplete project.json file:

{
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable"
},
"dependencies": {
"System.Runtime.Serialization.Primitives": "4.1.1",
"xunit": "2.1.0",
"dotnet-test-xunit": "1.0.0-rc2-192208-24"
},
"testRunner": "xunit",
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
}
},
"imports": [
"dotnet5.4",
"portable-net451+win8"
]
}
}
}
view raw project.json hosted with ❤ by GitHub

The only problem with this project.json is that we haven’t told the test runner where to get the library classes from. To do this, we just need to add a reference to the dependencies section:

"dependencies": {
"System.Runtime.Serialization.Primitives": "4.1.1",
"xunit": "2.1.0",
"dotnet-test-xunit": "1.0.0-rc2-192208-24",
"library": {
"target": "project"
}
}
view raw project.json hosted with ❤ by GitHub

This tells the test runner that we want a reference to ‘library’ and that it is a project. If we left the ‘target’ key empty, then the test runner would assume that we want a reference to a NuGet package called ‘library’, which will fail (unless there really is a NuGet package called ‘library’, but I haven’t found one).

Now that we have the project.json set up, we need to create a Tests.cs file and copy the content in from earlier in the blog post.

Here it is again, in case you don’t want to scroll back up:

Because, lets face it, developers are lazy

using Library;
using Xunit;
namespace TestApp
{
public class LibraryTests
{
// A stupid test class which assets that 19 + 23
// is 42, via the Thing class' Get method
// Note: The Fact attribute tells the xunit
// that TestThing is a single test.
[Fact]
public void TestThing()
{
Assert.Equal(42, new Thing().Get(19, 23));
}
}
}
view raw tests.cs hosted with ❤ by GitHub

Once you’ve saved Tests.cs, we can run the tests from the terminal. Assuming that you’re still in the test-library directory:

dotnet test
view raw bash.sh hosted with ❤ by GitHub

This will tell the .NET Core test runner to parse the project.json from within the test-library. From here, it will restore any NuGet packages, and it will build the Library class. Once it has restored the packages and built the library class, it will build the LibraryTests class.

After all of that, .NET Core’s test runner then start the LibraryTest class and load the compiled Library. Each method with the [Fact] attribute will be run, in turn.

Here’s an example output from when I ran the tests:

Project library (.NETStandard,Version=v1.6) was previously compiled. Skipping compilation.
Project test-library (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.
xUnit.net .NET CLI test runner (64-bit osx.10.12-x64)
Discovering: test-library
Discovered: test-library
Starting: test-library
Finished: test-library
=== TEST EXECUTION SUMMARY ===
test-library Total: 1, Errors: 0, Failed: 0, Skipped: 0, Time: 0.288s
SUMMARY: Total: 1 targets, Passed: 1, Failed: 0.program.cs
view raw bash.sh hosted with ❤ by GitHub

You’ll notice that compilation was skipped because I’d already done a build.

And that’s it. Adding tests is easy – just remember to use the [Fact] attribute for each new test, and remember to Assert on the result.

If this post has helped you out, please consider   Buy me a coffeeBuying me a coffee
Jamie Taylor
A .NET developer specialising in ASP.NET MVC websites and services, with a background in WinForms and Games Development. When not programming using .NET, he is either learning about .NET Core (and usually building something cross platform with it), speaking Japanese to anyone who'll listen, learning about languages, writing for his non-dev blog, or writing for a blog about video games (which he runs with his brother)