Course contentsShow
← C#
Lesson 1095 of 1,90810. Entity Framework Core ModelingFree lesson

Integration Testing with Dockerized Databases

Validate database interaction code against actual production-like RDBMS instances in tests.

In this lesson, you will learn how to shift your integration tests from fragile "In-Memory" providers to actual, production-like RDBMS instances running in Docker containers.

Why Avoid InMemory Providers?

Many developers start testing with the InMemory database provider, but it behaves differently than a real SQL Server or PostgreSQL instance. It lacks constraints, transaction support, and specific data type behaviors. If your application relies on unique indexes or specific stored procedures, an InMemory test might pass, while your production code fails.

The Docker Advantage

By using Testcontainers for .NET, you can spin up a real, ephemeral database container before your test suite starts. This ensures that your EF Core migrations, triggers, and SQL syntax are validated against the exact database engine your production environment uses.

Implementation Pattern

The goal is to map your DbContext to a containerized instance during the test setup phase.

public class UserIntegrationTests : IAsyncLifetime
{
    private readonly MsSqlContainer _dbContainer = new MsSqlBuilder().Build();

    public async Task InitializeAsync() => await _dbContainer.StartAsync();

    [Fact]
    public async Task Should_Save_User_To_Actual_Database()
    {
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseSqlServer(_dbContainer.GetConnectionString())
            .Options;

        using var context = new AppDbContext(options);
        await context.Database.MigrateAsync(); // Ensure schema matches

        context.Users.Add(new User { Name = "Test User" });
        await context.SaveChangesAsync();

        Assert.Equal(1, await context.Users.CountAsync());
    }

    public async Task DisposeAsync() => await _dbContainer.DisposeAsync();
}

Key Considerations

  1. Migrations: Always run your migrations against the container. This tests that your migration scripts are valid and that your entity configurations (e.g., column lengths, indexes) are actually applied to the RDBMS.
  2. Performance: Starting containers takes time. Group tests to share a single container instance per test class or collection to minimize overhead.
  3. Cleanup: Ensure your DisposeAsync correctly stops the container so you don't end up with "zombie" containers consuming system resources on your CI/CD runner.

Key Takeaway: Use Docker containers in your integration tests to ensure your EF Core models and queries behave exactly as they will in your production environment.