While unit tests verify isolated classes and methods by mocking dependencies, they often fail to catch integration bugs—such as misconfigured Entity Framework mappings, incorrect routing paths, broken JSON serialization settings, or faulty authentication pipelines.
To achieve true confidence in your application, you need Integration Testing, where your entire ASP.NET Core Web API boots up in an in-memory test host and executes end-to-end HTTP requests.
This article walks through a complete, production-grade implementation of integration testing using xUnit and WebApplicationFactory.
1. Architectural Strategy: The In-Memory Test Server
WebApplicationFactory<TProgram> spins up a real instance of your ASP.NET Core web host in memory without binding to an external port.
Real Middleware Pipeline: Requests pass through your actual authentication, rate-limiting, routing, and global exception-handling middleware.
Test Isolation: You can override dependency injection services on-the-fly—for instance, replacing a production database context with an in-memory database or a temporary Testcontainer instance.
2. Step 1: Installing Required Testing NuGet Packages
Create a separate xUnit test project (e.g., WebApi.IntegrationTests) and install the necessary packages:
Shell
dotnet add package Microsoft.AspNetCore.Mvc.Testing
dotnet add package xunit
dotnet add package xunit.runner.visualstudio
dotnet add package FluentAssertions
3. Step 2: Creating a Custom WebApplicationFactory
To isolate your tests from your production database, create a custom factory that replaces your database configuration with an isolated in-memory or SQLite database instance.
C#
// WebApi.IntegrationTests/CustomWebApplicationFactory.cs
using Infrastructure.Persistence;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace WebApi.IntegrationTests;
public class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// 1. Remove the existing production DbContext registration
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
// 2. Add a new AppDbContext using an in-memory database for testing
services.AddDbContext<AppDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryTestDb");
});
// 3. Build the service provider and seed test data
var sp = services.BuildServiceProvider();
using var scope = sp.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
db.Database.EnsureCreated();
// Seed sample data if needed...
});
}
}
4. Step 3: Writing End-to-End Integration Tests
Now, write your xUnit test class leveraging the custom factory to send real HTTP requests using an HttpClient.
C#
// WebApi.IntegrationTests/Controllers/ProductsControllerTests.cs
using System.Net;
using System.Net.Http.Json;
using FluentAssertions;
using Xunit;
namespace WebApi.IntegrationTests.Controllers;
public class ProductsControllerTests : IClassFixture<CustomWebApplicationFactory>
{
private readonly HttpClient _client;
public ProductsControllerTests(CustomWebApplicationFactory factory)
{
// Creates the in-memory HttpClient attached to our test host
_client = factory.CreateClient();
}
[Fact]
public async Task GetById_ReturnsNotFound_WhenProductDoesNotExist()
{
// Arrange
int nonExistentId = 9999;
// Act
var response = await _client.GetAsync($"/api/products/{nonExistentId}");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}
[Fact]
public async Task CreateProduct_ReturnsCreatedStatusCode_AndValidId()
{
// Arrange
var newProductCommand = new { Name = "Test Widget", Price = 49.99m, Stock = 10 };
// Act
var response = await _client.PostAsJsonAsync("/api/products", newProductCommand);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Created);
var createdId = await response.Content.ReadFromJsonAsync<int>();
createdId.Should().BeGreaterThan(0);
}
}
Conclusion
By leveraging WebApplicationFactory and xUnit, your integration test suite verifies your actual ASP.NET Core routing, middleware pipelines, model binding, and database logic end-to-end, guaranteeing production readiness before deployment.

Join the conversation! Your thoughts help the community grow.