Swagger-OpenAPI-Documentation :👈 👉:REST vs gRPC vs Message Brokers

πŸ§ͺ Unit Testing and Integration Testing in ASP.NET Core

API Testing in .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

Unit testing focuses on testing individual components of your application in isolation from infrastructure and dependencies.

  • Goal: Verify that a single method or unit of work performs the expected business logic.
  • Isolation: Dependencies like databases or HTTP context are mocked.
  • Tools: xUnit, NUnit, MSTest, Moq, NSubstitute etc...

    🧰 Tools Commonly Used

    • xUnit: Popular testing framework
    • Moq: For mocking dependencies
    • TestServer: For in-memory integration testing
    • FluentAssertions: For expressive assertions
  • Pattern: Arrange - Act - Assert (AAA).

Example: Unit Testing a Controller with Moq

// 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;
    }
}

βœ… Unit Testing Example

// 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 another Example

// 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

Integration testing validates that multiple components work together within the full application pipeline.

  • Goal: Ensure the full API pipeline works correctly.
  • Dependencies: Real or in-memory implementations (like InMemoryDb).
  • Tools: xUnit, NUnit, WebApplicationFactory.

βœ… Integration Testing Example

// 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);
    }
}

Another Example: Integration Testing with WebApplicationFactory

// 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());
    }
}

πŸ” What’s the Difference?

  • Unit Testing: Tests individual components (e.g., controllers, services) in isolation using mocks or stubs.
  • Integration Testing: Tests the full stack (controllers, middleware, database, etc.) to verify end-to-end behavior.

Key Differences Summary

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

βœ… When to Use Each

  • Unit Tests: For fast, isolated logic validation (services, helpers, controllers)
  • Integration Tests: For verifying real-world behavior (routing, middleware, DB access)

🚫 When Not to Use

  • Unit Tests: Avoid testing external systems (e.g., databases, APIs)
  • Integration Tests: Avoid over-testing trivial logic or duplicating unit tests

🌟 Advantages

  • Improves code reliability and maintainability
  • Facilitates refactoring with confidence
  • Helps catch bugs early in development

⚠️ Disadvantages

  • Requires setup and maintenance
  • Integration tests can be slower and more brittle
  • Over-testing can lead to false confidence or wasted effort

🧯 Precautions

  • Mock dependencies in unit tests to isolate logic
  • Use test databases or in-memory stores for integration tests
  • Run tests in CI/CD pipelines to catch regressions

🧠 Best Practices

  • Follow AAA pattern: Arrange, Act, Assert
  • Use meaningful test names and organize by feature
  • Keep tests fast and deterministic
  • Use coverage tools to identify gaps
Back to Index
Swagger-OpenAPI-Documentation :👈 👉:REST vs gRPC vs Message Brokers
*