IConfiguration vs IOptions NET
Synchronous and Asynchronous in .NET Core
Model Binding and Validation in ASP.NET Core
ControllerBase vs Controller in ASP.NET Core
ConfigureServices and Configure methods
IHostedService interface in .NET Core
ASP.NET Core request processing
| Swagger-OpenAPI-Documentation | REST vs gRPC vs Message Brokers | |
π§ͺ Unit Testing and Integration Testing in ASP.NET Core |
In .NET Core, testing APIs involves two main approaches: unit testing and integration testing. A common practice is to have more unit tests, a moderate number of integration tests, and very few end-to-end tests β known as the testing pyramid.
Unit testing focuses on testing individual components of your application in isolation from infrastructure and dependencies.
// Service Interface
public interface IProductService
{
Task<Product?> GetProductByIdAsync(int id);
}
// Controller
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
[HttpGet("{id}")]
public async Task<ActionResult<Product>> Get(int id)
{
var product = await _productService.GetProductByIdAsync(id);
if (product == null)
return NotFound();
return product;
}
}
// ProductService.cs
public class ProductService
{
public decimal CalculateDiscount(decimal price) => price * 0.9m;
}
// ProductServiceTests.cs
public class ProductServiceTests
{
[Fact]
public void CalculateDiscount_ReturnsCorrectValue()
{
var service = new ProductService();
var result = service.CalculateDiscount(100);
Assert.Equal(90, result);
}
}
// Unit Test
public class ProductsControllerTests
{
[Fact]
public async Task Get_ProductExists_ReturnsProduct()
{
var mockService = new Mock<IProductService>();
mockService.Setup(s => s.GetProductByIdAsync(1))
.ReturnsAsync(new Product { Id = 1, Name = "Test Product" });
var controller = new ProductsController(mockService.Object);
var result = await controller.Get(1);
var okResult = Assert.IsType<ActionResult<Product>>(result);
var product = Assert.IsType<Product>(okResult.Value);
Assert.Equal(1, product.Id);
}
}
Integration testing validates that multiple components work together within the full application pipeline.
// ProductsController.cs
[ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult Get() => Ok(new[] { "Laptop", "Phone" });
}
// ProductsIntegrationTests.cs
public class ProductsIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public ProductsIntegrationTests(WebApplicationFactory<Program> factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task Get_ReturnsProductList()
{
var response = await _client.GetAsync("/api/products");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
Assert.Contains("Laptop", content);
}
}
// Integration Test
public class ProductsApiIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public ProductsApiIntegrationTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
}
[Fact]
public async Task Get_ReturnsSuccessStatusCodeAndCorrectContentType()
{
var client = _factory.CreateClient();
var response = await client.GetAsync("/api/products");
response.EnsureSuccessStatusCode();
Assert.Equal("application/json; charset=utf-8", response.Content.Headers.ContentType?.ToString());
}
}
| Aspect | Unit Testing | Integration Testing |
|---|---|---|
| Scope | Tests a single component in isolation | Tests multiple components working together |
| Dependencies | Mocks and stubs | Real or in-memory services |
| Speed | Very fast | Slower due to setup |
| Confidence | Logic-level confidence | System-level confidence |
| Tools | xUnit, NUnit, Moq | WebApplicationFactory, xUnit |
| Swagger-OpenAPI-Documentation | REST vs gRPC vs Message Brokers | |