How to write xUnit tests for dependency injection services

Published: Monday 28 July 2025

How do you write xUnit tests for services that are used in dependency injection?

Mocking

One way to do is by mocking. Moq is a popular mocking framework for .NET. It allows you to create mock implementations of interfaces or classes. This is so you can test your code in isolation, without depending on actual implementations.

We want to unit test this CategoryService class:

// ICategoryStorageService.cs
public interface ICategoryStorageService
{
	List<CategoryTypeDto> Types { get; }
}

// ICategoryService.cs
public interface ICategoryService
{
	CategoryTypeDto? GetCategoryType(int id);
}

// CategoryService.cs
public class CategoryService : ICategoryService
{
	private readonly ICategoryStorageService _categoryComputersStorageService;

	public CategoryService([FromKeyedServices("computers")] 
		ICategoryStorageService categoryComputersStorageService)
	{
		_categoryComputersStorageService = categoryComputersStorageService;
	}

	public CategoryTypeDto? GetCategoryType(int id) => 
		_categoryComputersStorageService.Types.SingleOrDefault(s => s.Id == id);
}

Initialising the CategoryService class involves providing an implementation for the ICategoryStorageService type. We could create a new instance of that class that implements that interface. However, it might be coupled with other services meaning we would have to create instances for them as well. A better solution is to mock up an implementation of it.

To do that, we call the Mock class and wrap the service around the generic type that it requires.

We can then pass the mock object into the constructor's parameters:

// CategoryServiceTests.cs
public class CategoryServiceTests
{
	private readonly Mock<ICategoryStorageService> _categoryComputersStorageServiceMock;
	private readonly CategoryService _categoryService;

	public CategoryServiceTests()
	{
		_categoryComputersStorageServiceMock = new Mock<ICategoryStorageService>();
		_categoryService = new CategoryService(_categoryComputersStorageServiceMock.Object);
	}
}

Setting up mock returns

With the CategoryService instance set up in the xUnit test class, we can unit test the GetCategoryType method. This method relies on the Types property from the ICategoryStorage type.

We need to set up the Types property to return an response. We are able to do that because the _categoryComputersStorageService variable is a mock service.

In this example, we have set up the Types property in the ICategoryStorageService type to return an List<CategoryTypeDto> instance. The first index returns an id of 2 and a name of Computers, and the second returns an Id of 4 and a name of Software.

When that property is called within the unit tests, it will return the mock return value specified in the tests, meaning that we know the preferred outcome.

In the GetCategoryType_WhenCalled_ReturnsResult test, we pass in an Id of 2 and 7. We know that testing it with an Id of 2 will return an object instance as we've mocked it in the Types property in the ICategoryStorageService type. We also know that passing in an Id of 7 will return null as we haven't specified that Id within our Types property.

// CategoryServiceTests.cs
public class CategoryServiceTests
{
	private readonly Mock<ICategoryStorageService> _categoryComputersStorageServiceMock;
	private readonly CategoryService _categoryService;

	public CategoryServiceTests()
	{
		_categoryComputersStorageServiceMock = new Mock<ICategoryStorageService>();
		_categoryService = new CategoryService(_categoryComputersStorageServiceMock.Object);
	}

	[Theory]
	[InlineData(2)]
	[InlineData(7)]
	public void GetCategoryType_WhenCalled_ReturnsResult(int id)
	{
		// Arrange
		_categoryComputersStorageServiceMock.Setup(s => s.Types)
			.Returns(new List<CategoryTypeDto>
			{
				new(2, "Computers"),
				new(4, "Software")
			});

		// Act
		var act = _categoryService.GetCategoryType(id);

		// Assert
		if (id == 2)
		{
			Assert.Equivalent(act, new CategoryTypeDto(2, "Computers"));
		}
		else
		{
			Assert.Null(act);
		}
	}
}

Other tests

We can also do a similar thing for this web API controller:

// WebApiController.cs
[ApiController]
[Route("api/[controller]")]
public class WebApiController : ControllerBase
{
	private readonly ICategoryService _categoryService;
	private readonly ICategoryStorageService _categoryComputerStorageService;

	public WebApiController(
		ICategoryService categoryService,
		[FromKeyedServices("computers")] ICategoryStorageService categoryComputerStorageService
	)
	{
		_categoryService = categoryService;
		_categoryComputerStorageService = categoryComputerStorageService;
	}

	[HttpGet("category-type")]
	public IActionResult GetCategoryType()
	{
		return Ok(new
		{
			Category = _categoryService.GetCategoryType(2),
			KeyedCategory = _categoryComputerStorageService.Types.SingleOrDefault(c => c.Id == 2)
		});
	}
}

Initialising the WebApiController class involves providing an implementation for the ICategoryService and ICategoryStorageService types which we have created mock instances for. Within the unit test, we have set up the GetCategoryType method with a parameter of 2 to return a particular CategoryTypeDto instance. We've also done the same with the Types property in the ICategoryStorageService type.

When these methods are called within the unit tests, they will return the mock return values specified in the test and will therefore pass it:

// WebApiControllerTests.cs
public class WebApiControllerTests
{
	private readonly Mock<ICategoryService> _categoryServiceMock;
	private readonly Mock<ICategoryStorageService> _categoryComputerStorageServiceMock;
	private readonly WebApiController _webApiController;

	public WebApiControllerTests()
	{
		_categoryServiceMock = new Mock<ICategoryService>();
		_categoryComputerStorageServiceMock = new Mock<ICategoryStorageService>();

		_webApiController = new WebApiController(_categoryServiceMock.Object, _categoryComputerStorageServiceMock.Object);
	}

	[Fact]
	public void GetCategoryType_WhenCalled_ReturnsResult()
	{
		// Arrange
		_categoryServiceMock.Setup(s => s.GetCategoryType(2))
			.Returns(new CategoryTypeDto(2, "Computers"));

		_categoryComputerStorageServiceMock.Setup(s => s.Types)
			.Returns(new List<CategoryTypeDto>
			{
				new(2, "Computers"),
				new(4, "Software")
			});

		// Act
		var act = _webApiController.GetCategoryType();

		// Assert
		Assert.IsType<OkObjectResult>(act);
		Assert.Equivalent(((OkObjectResult)act).Value, new
		{
			Category = new CategoryTypeDto(2, "Computers"),
			KeyedCategory = new CategoryTypeDto(2, "Computers")
		});
	}
}

Testing real instances

If you are looking to add integration tests, you may wish to test real instances of the services that are injected as part of dependency injection.

To do that, you can create your own service collection, add the services and then build the service provider. You can then use that service provider instance to resolve any instances by calling either the GetService, GetRequiredService, GetKeyedService or GetRequiredKeyedService methods.

// WebApiControllerRealTests.cs
public class WebApiControllerRealTests
{
	private readonly WebApiController _webApiController;

	public WebApiControllerRealTests()
	{
		var services = new ServiceCollection();
		services.AddScoped<ICategoryService, CategoryService>();
		services.AddKeyedSingleton<ICategoryStorageService, CategoryComputersStorageService>("computers");

		var serviceProvider = services.BuildServiceProvider();

		var categoryService = new CategoryService(
				serviceProvider.GetRequiredKeyedService<ICategoryStorageService>("computers")
		);
		var categoryComputerStorageService = new CategoryComputersStorageService();

		_webApiController = new WebApiController(categoryService, categoryComputerStorageService);
	}

	[Fact]
	public void GetCategoryType_WhenCalled_ReturnsResult()
	{
		// Act
		var act = _webApiController.GetCategoryType();

		// Assert
		Assert.IsType<OkObjectResult>(act);
		Assert.Equivalent(((OkObjectResult)act).Value, new
		{
			Category = new CategoryTypeDto(2, "Computers"),
			KeyedCategory = new CategoryTypeDto(2, "Computers")
		});
	}
}

Because of this, you don't have to do any setup within the class members as you are not mocking any services. However, be mindful that you'll need to create new instances for each of the classes that are dependent on each other. This could become complex if you have many services. You may also come into issues if they rely on external sources such as a database.

Watch the video

Watch the video where we show you how to write these xUnit tests. We also show you how to create an xUnit test project and how to run them:

And if you want to run these xUnit tests for yourself, you can download the code example.