Types of Unit Testing in ASP.NET Core with xUnit

In this article, let's discuss the different types of methods and how do we write unit tests for these methods using xUnit in ASP.NET Core

So far We have been discussing about Unit Testing and how these tests can help in early detection of logical errors. we have also looked at the two major kinds of unit testing patterns: faking unit tests and mocking unit tests. We have looked in-detail about how Mocking Unit Tests can help at unit testing components with the respective dependencies simulated and how Moq framework helps in achieving mocking dependencies in a simpler way.

When it comes to functionalities which are encapsulated in the form of methods, we have returning methods and void methods. While unit testing a returning method is a simpler deal; testing a void method which does some operation but doesn’t return anything to indicate the calling component of its result is dealt in a different way.

Classification of Methods

Conceptually, there are two types of methods: Interacting methods and Computing methods. These are differentiated based on the method signature; specifically the return type.

Interacting methods are commands using which we instruct the system to do something; like Add or Delete a record, Print a statement and so on. These command methods don’t necessarily bear a return type – they don’t return anything. A delete command removes a record and it completes its responsibility – there’s no need to return anything.

Computing methods are query methods which we use to request the application to return some data: say a list of records for a given criteria or simply fetch all the types which exist in a system. These methods might have a parameter be passed as a criteria but do return data.

Since we have two types of method behaviours, we have two types of unit tests depending on what type of method we are subjecting our tests to:

1 – Interaction Tests

These test the interaction of a method onto the system. Since these are void methods, we need to verify the invocation of a method within these tests.

2 – Computation Tests

These test the computation of a method by virtue of its parameters. Since these return data, we can simply assert the returned data against a valid sample set.

Let’s look into how we can write such tests in our ReaderApi using xUnit and Moq.

Dotnet CLI and Visual Studio Code

The latest versions of Visual Studio Code (the light weight cross platform code editor from Microsoft) come integrated with Test Runners and Dotnet Core CLI, and so for this demonstration we shall make use of these tools for development.

Step 1 – Create a Test Project after pointing to the project folder, open a terminal window and type the below command:

    > dotnet new sln --name ReaderFactoryApp

The command creates a new solution file. Now we add two projects to the created solution. We first create an API project which holds our ReaderFactory and API logic, and another project which contains the test logic.

    > dotnet new webapi --name ReaderFactoryApp.Api --output ./Api
    > dotnet new xunit --name ReaderFactoryApp.Tests ./Tests

The above command creates two new projects under respective folders.

    > dotnet sln ./ReaderFactoryApp.sln add ./Api/ReaderFactoryApp.Api.csproj
    > dotnet sln ./ReaderFactoryApp.sln add ./Tests/ReaderFactoryApp.Tests.csproj

The above commands add the created projects of API and Tests onto the created solution file.

We’d like to test the functionality of ReaderFactory.Create() method which returns a new IReader instance for a given input.

public class ReaderFactory : IReaderFactory
{
    public IReader Create(int tierId)
    {
        IReader reader = null;

        switch(tierId) 
        {
            case 1:
                reader = new TierOneReader();
                break;
            case 2:
                reader = new TierTwoReader();
                break;
            case 3:
                reader = new TierThreeReader();
                break;
            case 4:
                reader = new TierFourReader();
                break;
            default:
                {
                    Print("No Matching Readers Found");
                    reader = new NoReader();
                }
                break;
        }

        return reader;
    }

    public void Print(string message)
    {
        Console.WriteLine(message);
    }
}

Create a new Unit Test file ReaderFactoryUnitTests which holds the unit tests we write for the ReaderFactory class.

public class ReaderFactory_UnitTests
{
    [Fact]
    public void Create_WhenCalled_ReturnsNoReader()
    {
        //Arrange
        var factory = new ReaderFactory();
        int tierId = 5;

        //Act
        var reader = factory.Create(tierId);

        //Assert
        // Assert.IsType(typeof(NoReader), reader);
        Assert.True(reader.GetType() == typeof(NoReader));
    }
}

From the above test method, We can assert the type of returned instance should be NoReader when a tierId of value above four is passed. This a simple example which tests the computation logic of a method and hence this is a computation test.

Now let’s consider the functionality at the level of ReaderController.Read() method where we would want to test when the Read() method invokes the method Create() over the factory instance, whether the Print() method is invoked or not.

public class ReaderController : Controller 
{
    [Route("{tierId}")]
    public IEnumerable<Reader> Read(int tierId)
    {
        var reader = factory.Create(tierId);
        return reader.ReadContent();
    }
}

This Print() method is a void method in this case; a command that prints over the console. Since we want to test an invocation of a method which doesn’t return any value to assert for variation, this comes under the category of an Interactive Test which we shall look in the next article.


Buy Me A Coffee

Found this article helpful? Please consider supporting!

Ram
Ram

I'm a full-stack developer and a software enthusiast who likes to play around with cloud and tech stack out of curiosity. You can connect with me on Medium, Twitter or LinkedIn.

Leave a Reply

Your email address will not be published. Required fields are marked *