Unit testing is a critical aspect of software development that ensures individual components function as expected. In ASP.NET Core, unit tests help validate API controllers, services, and business logic. The combination of xUnit (a popular unit testing framework) and Moq (a mocking library) provides an efficient way to test ASP.NET Core APIs.
This guide covers:
- Setting up an xUnit test project
- Writing unit tests for API controllers
- Using Moq for dependency injection testing
By the end of this article, you will have a comprehensive understanding of how to effectively unit test your ASP.NET Core APIs using xUnit and Moq.
1. Setting Up an xUnit Test Project
1.1 Install Required Packages
To begin unit testing, you need an xUnit test project. Create a new test project using the .NET CLI:
mkdir UnitTestingDemo
cd UnitTestingDemo
dotnet new xunit -n UnitTestingDemo.Tests
Navigate to the test project and install required dependencies:
dotnet add package Microsoft.AspNetCore.Mvc.Core
dotnet add package Microsoft.AspNetCore.Mvc.Testing
dotnet add package Moq
dotnet add package xunit
dotnet add package FluentAssertions
1.2 Create a Sample API Controller
Assume you have the following API controller:
[ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
var product = _productService.GetProductById(id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
}
1.3 Create the Service Interface and Implementation
public interface IProductService
{
Product GetProductById(int id);
}
public class ProductService : IProductService
{
private readonly List<Product> _products = new()
{
new Product { Id = 1, Name = "Laptop", Price = 1000 },
new Product { Id = 2, Name = "Phone", Price = 500 }
};
public Product GetProductById(int id)
{
return _products.FirstOrDefault(p => p.Id == id);
}
}
1.4 Define the Product Model
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
2. Writing Unit Tests for API Controllers
2.1 Create a Test Class
Inside the UnitTestingDemo.Tests
project, create a file ProductsControllerTests.cs
:
using Xunit;
using Moq;
using Microsoft.AspNetCore.Mvc;
using System;
using UnitTestingDemo.Controllers;
using UnitTestingDemo.Services;
using UnitTestingDemo.Models;
public class ProductsControllerTests
{
private readonly Mock<IProductService> _mockService;
private readonly ProductsController _controller;
public ProductsControllerTests()
{
_mockService = new Mock<IProductService>();
_controller = new ProductsController(_mockService.Object);
}
}
2.2 Test for a Valid Product ID
[Fact]
public void GetProduct_ReturnsOk_WithValidProduct()
{
// Arrange
var product = new Product { Id = 1, Name = "Laptop", Price = 1000 };
_mockService.Setup(s => s.GetProductById(1)).Returns(product);
// Act
var result = _controller.GetProduct(1);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnedProduct = Assert.IsType<Product>(okResult.Value);
Assert.Equal(product.Id, returnedProduct.Id);
}
2.3 Test for an Invalid Product ID
[Fact]
public void GetProduct_ReturnsNotFound_WhenProductDoesNotExist()
{
// Arrange
_mockService.Setup(s => s.GetProductById(10)).Returns((Product)null);
// Act
var result = _controller.GetProduct(10);
// Assert
Assert.IsType<NotFoundResult>(result);
}
3. Using Moq for Dependency Injection Testing
3.1 Mocking Service Dependencies
When writing unit tests, you often need to mock dependencies. Moq helps create mock implementations of services, repositories, or external dependencies.
3.2 Mocking GetProductById Method
_mockService.Setup(s => s.GetProductById(It.IsAny<int>()))
.Returns((int id) => new Product { Id = id, Name = "Mock Product", Price = 999 });
3.3 Testing Exception Handling
[Fact]
public void GetProduct_ReturnsServerError_OnException()
{
// Arrange
_mockService.Setup(s => s.GetProductById(It.IsAny<int>()))
.Throws(new Exception("Unexpected error"));
// Act
var result = _controller.GetProduct(1);
// Assert
Assert.IsType<ObjectResult>(result);
var objectResult = result as ObjectResult;
Assert.Equal(500, objectResult.StatusCode);
}
Conclusion
Unit testing ASP.NET Core APIs with xUnit and Moq ensures reliability and maintainability. In this guide, we covered:
- Setting up an xUnit test project
- Writing unit tests for API controllers
- Using Moq for dependency injection testing
By implementing unit tests, you can detect bugs early, improve code quality, and facilitate continuous integration. Start applying these techniques to your .NET projects for robust and error-free APIs!