Integration Testing C# with Docker: From A to Z
\n\nIntegration testing is a critically important stage in developing any serious C# application. It checks how various system components (database, external APIs, file systems) work together. However, setting up a test environment often becomes a headache: you need to spin up real servers, clean up data after tests, and deal with environment instability. Docker solves these problems by providing lightweight, isolated, and reproducible containers. In this article, we will break down how to set up integration testing C# with Docker from scratch to a working pipeline.
\n\nWhy Docker is Ideal for Integration Tests in C#?
\n\nThe traditional approach is to use local SQL Server databases or emulators of external services. This leads to problems:
\n- \n
- Data pollution: tests leave garbage in the database, affecting other tests. \n
- Environment incompatibility: one version of the DBMS on the developer's machine, another on the CI server. \n
- Difficulty of parallel execution: tests conflict over ports and resources. \n
Docker solves this simply: each test (or group of tests) gets its own isolated container with the required version of the database, Redis, or another service. Containers are lightweight, start in seconds, and are guaranteed to be destroyed after the tests finish. For .NET developers, this is especially relevant thanks to the Testcontainers for .NET library, which automates container management directly from the test code.
\n\nProject Setup: Adding Docker to C# Tests
\n\nFirst, let's create a project with integration tests. Let's assume we have a web API on .NET 8 that works with PostgreSQL. We need to test the user creation endpoint.
\n\nStep 1: Installing NuGet Packages
\nAdd the following packages to the test project:
\ndotnet add package Testcontainers.PostgreSql\ndotnet add package Microsoft.AspNetCore.Mvc.Testing\ndotnet add package FluentAssertions\n\nTestcontainers.PostgreSql is a wrapper over the Docker SDK that manages PostgreSQL containers. Microsoft.AspNetCore.Mvc.Testing allows you to run the web application in memory for testing.
Step 2: Creating a Base Class for Tests
\nLet's create an abstract class that will spin up a database container before the tests:
\nusing Testcontainers.PostgreSql;\nusing Xunit;\n\npublic abstract class IntegrationTestBase : IAsyncLifetime\n{\n private readonly PostgreSqlContainer _postgresContainer;\n\n protected string ConnectionString => _postgresContainer.GetConnectionString();\n\n protected IntegrationTestBase()\n {\n _postgresContainer = new PostgreSqlBuilder()\n .WithImage("postgres:16-alpine")\n .WithDatabase("testdb")\n .WithUsername("test")\n .WithPassword("test123")\n .Build();\n }\n\n public async Task InitializeAsync()\n {\n await _postgresContainer.StartAsync();\n }\n\n public async Task DisposeAsync()\n {\n await _postgresContainer.StopAsync();\n }\n}\n\nThis class implements IAsyncLifetime from xUnit, which guarantees starting the container before all tests and stopping it after. The ConnectionString is dynamically obtained from the container — the port can be any available one.
Step 3: Writing a Test with Testcontainers and WebApplicationFactory
\nNow let's write the test itself. We will use WebApplicationFactory to create a test HTTP client:
using Microsoft.AspNetCore.Mvc.Testing;\nusing FluentAssertions;\nusing System.Net.Http.Json;\n\npublic class UserApiTests : IntegrationTestBase\n{\n private readonly WebApplicationFactory<Program> _factory;\n\n public UserApiTests()\n