ASP.NET Core Integration Testing with a Real Database

ASP.NET Core Integration Testing with a Real Database

ASP.NET Core Integration Testing with a Real Database

Unit tests verify business rules quickly, but they do not exercise routing, model binding, authentication, and SQL behavior together. An integration test starts the application through its real hosting pipeline and calls it over HTTP. Migration failures, incorrect dependency registrations, and provider-specific query problems can therefore be detected before deployment.

Why isolation matters

Pointing tests at a shared developer database makes results depend on execution order and existing records. A disposable database for each test run is safer. Containers let local machines and CI agents use the same SQL version. When the run ends, the container disappears and there is no manually maintained test data left behind.

An end-to-end API test

This test sends a real HTTP request and verifies both the status code and response contract. The custom factory replaces the application connection string with a temporary database and applies migrations before tests run.

public sealed class OrdersApiTests : IClassFixture<BlogApiFactory>
{
    private readonly HttpClient _client;

    public OrdersApiTests(BlogApiFactory factory)
        => _client = factory.CreateClient();

    [Fact]
    public async Task Create_order_returns_created_resource()
    {
        var response = await _client.PostAsJsonAsync(
            "/api/orders", new { customerId = 42, total = 125 });

        response.StatusCode.Should().Be(HttpStatusCode.Created);
        var order = await response.Content.ReadFromJsonAsync<OrderResponse>();
        order!.CustomerId.Should().Be(42);
    }
}

The test does not invoke a controller method directly. Middleware, JSON serialization, validation, services, and the database all participate. One passing test cannot prove the whole system, but it provides strong protection against broken contracts between layers.

Building a reliable suite

  1. Apply the application's real migrations to the test database automatically.
  2. Isolate tests with transaction rollback or unique data keys.
  3. Cover 400, 401, 403, 404, and conflict responses in addition to successful requests.
  4. Replace clocks and external HTTP services with controlled test implementations.
  5. Preserve container logs as CI artifacts when a test run fails.

Common mistakes

  • Using an in-memory provider and assuming SQL Server queries and constraints are being validated.
  • Sharing records between tests and introducing random failures during parallel execution.
  • Testing every small detail through integration tests and creating a slow, fragile test pyramid.

Conclusion

A productive strategy combines many fast unit tests with a smaller set of integration tests for critical flows. Registration, authorization, payments, and data-integrity boundaries deserve real infrastructure. Disposable databases make those tests repeatable locally and portable to continuous integration.

0 Yorumlar

Yorum Yaz

E-posta adresiniz yayınlanmayacaktır.