Unit Testing ASP.NET Core APIs with xUnit & Moq | Step-by-Step Guide

Unit Testing ASP.NET Core APIs with xUnit & Moq | Step-by-Step Guide

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:

  1. Setting up an xUnit test project
  2. Writing unit tests for API controllers
  3. 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!

Sandip Mhaske

I’m a software developer exploring the depths of .NET, AWS, Angular, React, and digital entrepreneurship. Here, I decode complex problems, share insightful solutions, and navigate the evolving landscape of tech and finance.

Post a Comment

Previous Post Next Post