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

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

xUnit Unit Testing  • Posted one month ago

Integration tests are helpful in assessing how well two or more components work together as a whole for a desired functionality. The unit tests on the other hand, work at a lower level than the integration tests and are helpful in verifying the functionality of a single component or a unit.

What is a Unit?

A unit can be termed as the smallest possible piece of functionality which needs to be tested for a desired result for a varied set of inputs - real or mock. A unit can be independent of other units or components, or can contain one or more dependencies. But in unit tests we verify the functionality of a particular unit of functionality without focusing on its behavior when interacting with other real components. In general, a functional component can be made up of one or more units, with necessary communications in place to perform a specific functionality together.

A simple example can be a Logger component, which can comprise of a Logger unit that captures the logs from the application and a DataWriter unit that writes to an external data store. These two otherwise individual units work together to achieve the Logger component.

In order to test a Unit we pass in a fake or a mock dependency if required, to that particular function or class and check for an expected result. If success would mean that the given Unit Under Test (UUT) has no logical errors for itself (ignoring the external impact). In our context, we can refer an unit to an isolated logical class ignoring its dependencies.

Unit Testing Helps Developers

Since unit tests work at the lowest level possible in the code and operate on a single functionality alone, we get to see any logical errors which might otherwise go unnoticed in our code. Testing and rectifying such issues can help reducing the bugs in the application which might otherwise increase the time and effort for fixing at later stages in the development. Also, unit tests can indirectly help in writing testable code thereby resulting in a loosely coupled and structured components.

The practice of writing unit tests lead to Test Driven Development (TDD) in which we develop logic based on the probable unit tests which can occur on the components being developed for the requirements. We start by writing in negative tests and develop logic which passes these tests and then move forward and then iterate the process again. This results in a much cleaner and maintainable code.

Unit Testing and ASP.NET Core:

Irrespective of whatever language we use, the unit tests follow the paradigm of 3As. They are Arrange, Act and Assert. This principle applies for all kinds of tests, be it writing Integration Tests or Unit Tests.

There are multiple testing frameworks available for writing unit tests in ASP.NET Core such as NUnit, MSTest, MSTest2 and xUnit. All of these follow the same approach, while there are quite a few minor differences in their syntax and features.

xUnit is an open-source framework built from NUnit developed by the founders of NUnit framework. It is fast, simple to write and it has good integration with dotnet core, which is why we are interested in writing unit tests in xUnit.

We can create a new xUnit Test Project by using the below command in dotnet core CLI:


> dotnet new xunit --name ReaderApi.Tests

*where the name of the test project is based on the solution name or the project name for which we are writing the tests

Convention: Once a new project is created, we can start off by creating a new Test class. We prefix the class name by the name of the unit which we are writing tests for.

Example:


public class ReaderController_UnitTests 
{
	//unit tests go in here
}

Let's take the previous example of a ReadersController which has an API to return the list of all readers from the database for a particular room. Let's assume that we would want to test the success scenario for the API, which returns a List if readers are available and an empty set when no readers are available. We separate the domain logic from our Controller logic by employing a Repository class with its abstraction IReaderRepo as below:


public class ReaderRepo 
{
	public IEnumerable<Reader> GetAllReaders(string initial) 
	{
		using (ReaderContext ctx = new ReaderContext()) 
		{
        ctx.Readers.Where(x => x.IsActive == true 
            && x.Initial == initial);
		}
	}
}

[Route("api/[controller]")]
[ApiController]
public class ReadersController : ControllerBase
{
	[HttpGet]
	[Route("{id}")]
	public IEnumerable<Reader> GetReaders(string id)
	{
		var repo = new ReaderRepo();
		return repo.GetAllReaders(id);
	}
}

The function in the above code directly invokes a new object of type ReaderRepo, making it less unit testable. Unit Testing a component requires us to make sure that the Unit Under Test (UUT) is completely isolated from any external dependencies or any other units. This also violates the principles of Dependency Inversion and Single Responsibility.

To make the functionality unit testable, we shall decouple the component from its dependencies as below:

public interface IReaderRepo 
{
    IEnumerable<Reader> GetAllReaders(string initial);
}

public class ReaderRepo : IReaderRepo 
{

	public ReaderRepo()
	{
	}

	public IEnumerable<Reader> GetAllReaders(string initial) 
	{
		using (ReaderContext ctx = new ReaderContext()) 
		{
        ctx.Readers.Where(x => x.IsActive == true 
          && x.Initial == initial);
		}
	}
}

[Route("api/[controller]")]
[ApiController]
public class ReadersController : ControllerBase
{
    IReaderRepo repo;

    public ReadersController(IReaderRepo repo)
    {
        this.repo = repo;
    }

	[HttpGet]
	[Route("{id}")]
	public IEnumerable<Reader> GetReaders(string id)
	{
		return this.repo.GetAllReaders(id);
	}
}

After refactoring, the class ReadersController has a single dependency of type IReaderRepo which is passed on as a constructor parameter. In order to unit test the functionality GetReaders() of this class, we would need to provide an implementation object to the class. Now in unit tests, we ignore the dependencies of the Unit Under Test by providing a fake dependency or a mock dependency.

a unit test for this class can look like below:


public class ReaderController_UnitTests 
{
	[Fact]
	public void GetReadersWhenCalledReturnListOfReaders()
	{
		//logic to test		
	}
}

There are three things to observe from the above code:

  1. decorator [Fact]
  2. method signature and
  3. naming.

Guidelines for a Test Method:

A method can be called a "Test Method" or the one which tests the targeting functionality, only if it is designed inline with the following guidelines:

  1. A Test method should always be public and should not return anything (void or Task in case of testing an async method)
  2. A Test method in xUnit should always bear a decorator Fact for hard coded values, and Theory if the test input values are varied.
  3. A Test method name must explain the functionality on which the test being carried, the kind of input we're passing and the expected outcome.

A UnitTest Method for the class ReadersController looks like below:


public class ReaderController_UnitTests 
{
	[Fact]
	public void GetReadersWhenCalledReturnListOfReaders()
	{
		//Arrange the resources
		var repo = new FakeReaderRepo();
		var controller = new ReadersController(repo);
		string initial = "A";

		//Act on the functionality
		List<Reader> response = controller.GetAllReaders(initial);

		//Assert the result against the expected
		Assert.True(response.Count() > 0);
	}
}

*we need to add a reference to the project ReaderApi where the ReadersController and IReaderRepo classes exist for referencing the types in the test logic.

Within the TestClass constructor, instead of passing the actual ReaderRepo class we pass the instance of a FakeReaderRepo which also implements the IReaderRepo interface we have created inorder to reduce the dependency on the ReadersController class. This simulates the functionality of a ReaderRepo while not necessarily being a real-world implementation. This is what we call as a "Fake".

the FakeReaderRepo implementation can be as follows:


public class FakeReaderRepo : IReaderRepo 
{

	public FakeReaderRepo()
	{
	}

	public IEnumerable<Reader> GetAllReaders(string initial) 
	{
		return new List<Reader>() {
			new Reader {
				Id = 1001,
				Name = "Mr.A",
				Initial = "A"
			}
		};
	}
}

The tests are run by using the below CLI command:


> dotnet test

This way, we can create a UnitTest method for a class by faking its dependencies via abstractions. While it is simple to fake a given dependency, its a good practice to Mock a dependency than faking it.

For this we make use of a mocking framework called Moq which simplifies passing in mock dependencies to our Unit Under Test (UUT) in place of a real-world dependencies. Combining xUnit with Moq framework makes unit testing components with potential dependencies easier and simpler. We shall look more about how we can use Moq and xUnit together in our Unit Testing methods in the upcoming articles.

xunit moq .net core example xunit mocking xunit and moq .net core xunit verify xunit moq .net core xunit test void method xunit moq .net core xunit mock unit testing .net core 3 applications with xunit .net & moq moq xunit .net core xunit assert void method xunit mock xunit and moq asp.net core xunit xunit mock interface xunit xunit test void method c# xunit tutorial .net core xunit dependency injection .net core xunit .net core dependency injection webapplicationfactory xunit jenkins xunit c# xunit moq