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
- 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.
- Performance: Starting containers takes time. Group tests to share a single container instance per test class or collection to minimize overhead.
- Cleanup: Ensure your
DisposeAsynccorrectly 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.