Unit Testing with .NET Core

Jeremy Morgan - Aug 8 '19 - - Dev Community

Follow me on Twitter! I'm happy to hear what you think of this, and suggestions for new articles.

It's hard to find serious software these days without unit tests. So if you're considering a migration of your .NET applications to .NET Core, or considering starting a new project in .NET Core, one of your first questions may be: can we unit test it? The answer is yes, and in this tutorial I'll show you how easy it is to set up unit testing in .NET Core with the dotnet CLI.

Step 1: Let's Build a Class Library

For this tutorial we'll build a simple class library called "string modifier". As the name suggests, it will be used to modify strings. We'll use the .NET Core CLI to create a new class library.

dotnet new classlib -o stringModifier
Enter fullscreen mode Exit fullscreen mode

You should see something that looks like this:

This uses the dotnet CLI to create a new class library in the folder named stringModifier. It creates an application that is essentially just a basic class. We're going to name this something a bit more useful though.

rename your class to ChangeMyString. It should look like this:

using System;

namespace stringModifier
{
    public class ChangeMyString
    {
    }
}
Enter fullscreen mode Exit fullscreen mode

We're going to create a library that modifies strings for us, so let's add a couple methods to our string modifier library.

public static string makeAllUppercase(string inputString){
    return inputString.ToUpper();
}
Enter fullscreen mode Exit fullscreen mode

This method will make any string you send it all uppercase.

The next method will reverse a string:

public static string makeReverse(string inputString){
    char[] arr = inputString.ToCharArray();
    Array.Reverse(arr);
    return new string(arr);
}
Enter fullscreen mode Exit fullscreen mode

Now these are obviously for demo purposes and easy to see what they do, but let's do a quick smoke test with them.

Let's make sure the application builds. Run the following in the folder your project is located in:

dotnet build
Enter fullscreen mode Exit fullscreen mode

It should look like this:

And we can go into our BIN folder and verify the artifacts were generated:

Now you're ready to go.

Step 2: The Smoke Test

Let's build a quick console application that calls the class library just to do a quick visual test of functionality. Exit out of the project folder, and we want to use the dotnet CLI to create a runner for this.

dotnet new console -n smrunner
Enter fullscreen mode Exit fullscreen mode

This will create a simple "hello world" application for us. Open up Project.cs and add the following using:

using stringModifier;
Enter fullscreen mode Exit fullscreen mode

then add the following into the Main method:

Console.WriteLine("Hello World!");
Console.WriteLine(ChangeMyString.makeAllUppercase("Hello World!"));
Console.WriteLine(ChangeMyString.makeReverse("Hello World!"));
Enter fullscreen mode Exit fullscreen mode

The entire method should look this:

using System;
using stringModifier;

namespace smrunner
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            Console.WriteLine(ChangeMyString.makeAllUppercase("Hello World!"));
            Console.WriteLine(ChangeMyString.makeReverse("Hello World!"));
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, back at the command line, let's add a reference to our class library:

dotnet add reference "../stringModifer/stringModifier.csproj"
Enter fullscreen mode Exit fullscreen mode

This will add the reference to the class library automatically. You could also manually put in the following in the .csproj file:

<ItemGroup>
    <ProjectReference Include="..\stringModifier\stringModifier.csproj" />
</ItemGroup>
Enter fullscreen mode Exit fullscreen mode

Either way is fine. Now we're ready to run it:

dotnet run
Enter fullscreen mode Exit fullscreen mode

You should see something that looks like this:

Now we can verify that the methods work as we expect them to. But we'll add some unit tests to be sure.

Remember: Unit tests are not just to check the individual functionality of a method. They also check the functionality does not change when other parts of the application change. This is part of what makes them so important.

Step 3: Building the Unit Tests

Let's build a separate project for the unit tests. This is the project you'll run after a build, or preferably automatically on a build server every time you deploy.

Exit out of the directory you were just in and let's use the dotnet CLI to create yet another project. This will be an MSTEST Unit Test project.

dotnet new mstest -o libraryTester
Enter fullscreen mode Exit fullscreen mode

This creates a unit test project in the libraryTester directory. Now we open up UnitTest1.cs, and this should look really familiar to you if you've built unit tests before. This looks identical to tests in conventional .Net.

So let's add a couple tests.

First we need to add a using statement to include the library.

using stringModifier;
Enter fullscreen mode Exit fullscreen mode

The first tests we want to run are to make sure the test method returns a string. Now we know the return type of these methods, but we want to make a test to catch it if everyone ever changes that:

[TestMethod]
public void TestMUCReturnsString()
{
    string sample = "Test String";
    Assert.IsInstanceOfType(ChangeMyString.makeAllUppercase(sample), typeof(string));
}

[TestMethod]
public void TestMRReturnsString()
{
    string sample = "Test String";
    Assert.IsInstanceOfType(ChangeMyString.makeReverse(sample), typeof(string));
}
Enter fullscreen mode Exit fullscreen mode

Next we want to test the actual functionality of the methods. So we have a couple test cases here to do that.

[TestMethod]
public void TestMakeAllUppercase()
{
    string sample = "Test String";
    Assert.AreEqual(ChangeMyString.makeAllUppercase(sample), "TEST STRING");
}

[TestMethod]
public void TestMakeReverse()
{
    string sample = "Test String";
    Assert.AreEqual(ChangeMyString.makeReverse(sample), "gnirtS tseT");
}
Enter fullscreen mode Exit fullscreen mode

Now what we are doing here is asserting what we expect the method to return, and then inserting test string in the method to see if it matches what we expect.

This is what the final file should look like:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using stringModifier;
namespace libraryTester
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMUCReturnsString()
        {
            string sample = "Test String";
            Assert.IsInstanceOfType(ChangeMyString.makeAllUppercase(sample), typeof(string));
        }

        [TestMethod]
        public void TestMRReturnsString()
        {
            string sample = "Test String";
            Assert.IsInstanceOfType(ChangeMyString.makeReverse(sample), typeof(string));

        }

        [TestMethod]
        public void TestMakeAllUppercase()
        {
            string sample = "Test String";
            Assert.AreEqual(ChangeMyString.makeAllUppercase(sample), "TEST STRING");
        }

        [TestMethod]
        public void TestMakeReverse()
        {
            string sample = "Test String";
            Assert.AreEqual(ChangeMyString.makeReverse(sample), "gnirtS tseT");
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

These are some pretty basic tests, but should be good for now. Let's try it out.

Step 4: Building the Unit Tests

Now we're in the library tester folder, and In order to build this, we'll have to add a reference again.

dotnet add reference "../stringModifier/stringModifier.csproj"
Enter fullscreen mode Exit fullscreen mode

Now let's test the build.

dotnet build
Enter fullscreen mode Exit fullscreen mode

It looks like we don't have any failures here so we should be good to go.

To run the tests we'll type in

dotnet test
Enter fullscreen mode Exit fullscreen mode

And our tests are successful! We can see that 4 tests were run and everything is green.

Now, let's break a test just to see what that looks like.

Let's go back to our original library. Change this:

public static string makeAllUppercase(string inputString){
    return inputString.ToUpper();
}
Enter fullscreen mode Exit fullscreen mode

to this:

public static string makeAllUppercase(string inputString){
    return inputString;
}
Enter fullscreen mode Exit fullscreen mode

Let's refactor the method to pass through the string instead of making it upper case. Save it and build it.

dotnet build
Enter fullscreen mode Exit fullscreen mode

And go back into our library tester folder, and run a test again:

dotnet test
Enter fullscreen mode Exit fullscreen mode

And now we have a failure. As you can see by the splash of red on the screen it threw an error. It expected test string to be in all caps, and it's not.

This is a great feature of unit tests, if some yahoo goes in and refactors something and breaks the functionality, your test will pick it up right away.

This is how easy it is to build unit tests in .NET Core!!

Note

I used Debian Linux for this tutorial, but you don't have to. Using the dotnet CLI this tutorial is the same in Windows, Linux, or OSX. That's the beauty of .NET Core!

Conclusion

Unit tests are super important. I won't use this tutorial as a lecture, but they're well worth your time and too frequently overlooked. Just because you're developing .NET Core apps doesn't mean they can't be unit tested, MSTEST seems to work very well with everything I've done with it so far. So do it!! Leave any feedback in the comments or Yell at me on Twitter

Further Reading

For a good intro to testing in .NET Core, check out this article just posted:

What's your .NET Core IQ??

My ASP.NET Core Skill IQ is 200. Not bad, but can you beat it? Click here to try

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .