We use cookies to provide you with a great user experience, analyze traffic and serve targeted promotions.   Learn More   Accept
Card image cap

Writing Unit Tests for Void Methods using xUnit, Moq and Dotnet Core CLI - Part Two

xUnit Unit Testing  • Posted 8 months ago

So far in our journey of writing unit tests for void methods or command methods, which are meant to invoke some command onto the system, we have been looking at the different types of methods and how writing unit tests would differ for each method types - the Interation tests and the Computation tests. For a Computation test we can simply assert the functionality by its return value and judge if the functionality has any issues.

But for Interaction tests where there are no return types, we assert the functionality of such methods by verifying whether a particular method has been called or not.

Let's assume the ReaderController has method Read() which internally invokes the ReaderFactory for a Reader instance based on the tierId parameter value.

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

We have previously written unit tests on the ReaderFactory class to assert the return object type for a passed in tierId. In this let's write unit tests on the method in ReaderController which has a dependency on the ReaderFactory via IReaderFactory interface injection.

Let's create a new file ReaderController_UnitTests which holds the same.

We can test two functional flows:

  1. We can check for the return data when a tierId is passed to the Read() method
  2. We can verify if the Create() method on ReaderFactory class is called when we pass in any tierId.

The former test would be a query test and the later is a verification test and hence an interaction test.

Let's add the package Moq to use in this project:

    > dotnet add package Moq

Let's add test logic to the two test scenarios:

public class ReaderController_UnitTests
{
    [Fact]
    public void Read_TierOneReader_WhenCalled_ReturnsNonEmptyList()
    {
        //Arrange
        var factoryMoq = new Mock<IReaderFactory>();
        factoryMoq.Setup(x => x.Create(It.IsAny<int>()))
            .Returns(new TierOneReader());

        var controller = new ReaderController(factoryMoq.Object);

        //Act
        var data = controller.Read(1).ToList();

        //Assert
        Assert.True(data.Count() > 0);
    }

    [Fact]
    public void Read_NullReader_WhenCalled_InvokesCreate()
    {
    
        //Arrange
        var factoryMoq = new Mock<IReaderFactory>();
        factoryMoq.Setup(x => x.Create(It.IsAny<int>()))
        .Returns(new NullReader());
        
        var controller = new ReaderController(factoryMoq.Object);

        //Act
        var data = controller.Read(1).ToList();

        //Assert
        factoryMoq.Verify(x => x.Create(It.IsAny<int>()));
        factoryMoq.Verify(x => x.Print(It.IsAny<string>()), Times.AtLeastOnce);
    }
}

We have used a Mock object of ReaderFactory to write these tests; in the actual flow we would inject an instance of ReaderFactory to the ReaderController class through constructor injection and we would like to use a mock implementation of the IReaderFactory which is substituted by a real ReaderFactory instance during runtime.

The second method Read_NullReader_WhenCalled_InvokesCreate() is the actual interaction method, wherein we verify whether the call to Create() method is invoked or not. There's another call to Print() method on the next line, which is actually called when there's no valid Reader available for the tierId.

public class ReaderFactory : IReaderFactory
{
    public IReader Create(int tierId)
    {
        if (tierId == 1)
            return new TierOneReader();
        else if (tierId == 2)
            ...
        else
            Print("No Matching Readers Found");
        return new NullReader();
    }

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

In this scenario we verify whether a call to Print() has been made, if the tierId has no valid if-else branch.

The Moq method factoryMoq.Verify(x => x.Print(It.IsAny()), Times.AtLeastOnce) takes an argument Times.AtLeastOnce which means that the call to the method Print() should happen atleast once during the invocation. Else the assertion would fail. Other flags available are Times.Ones, Times.AtmostOnce and such.

Running the Tests:

Before test execution, we would need to add reference for the project ./Api/ReaderFactoryApp.Api.csproj within the project ./Api/ReaderFactoryApp.Tests.csproj

we can do it by the command,

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

This adds a reference to the API project in test project and should remove all the reference errors. We might also get an error when accessing the controller class within the test project, we can resolve it by adding a reference to the package Microsoft.AspNetCore.Mvc.Core

    > dotnet add package Microsoft.AspNetCore.Mvc.Core

This should solve all the build errors, and we're good to go. Let's build the projects by running the command against the solution:

    > dotnet clean && dotnet build

We can observe that the Editor shows options for Run Test on top of each method and each class. We can run the tests by either clicking on the link above the methods

data/Admin/2019/11/Capture.PNG

or by running a command:

    > dotnet test

And the result would be as below:

data/Admin/2019/11/Capture2.PNG

In this way, we can write unit tests for command methods as well as query methods by using xUnit, Moq and dotnet CLI.

You might also be interested in these posts on Unit Testing and xUnit:

Writing Mocking unit tests in ASP.NET Core using xUnit and Moq

How to write unit tests in ASP.NET Core using xUnit - Getting Started

moq void method unit test void method c# unit test void method c# moq create unit test for void method c# xunit test void method how to test void methods c# assert void method c# unit test case for void method in c# unit test for void method c# xunit assert void method c# unit test void method moq mock void method unit test void method moq return void how to write unit test for void method c# test void method c# unit testing void methods assert for void method c# unit testing void methods c# xunit test void method c# how to unit test methods that return void c# unit test verify method called unit testing c# asp.net core