Integrationstests in C# mit Docker: Vollständiger Leitfaden

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten
uld().Be("Ivan"); savedUser.Email.Should().Be("ivan@example.com"); }}

Integration Testing C# with Docker: How to Get Rid of Environment Problems



Jeder C#-Entwickler kennt diese Situation: Tests laufen lokal einwandfrei, scheitern aber auf dem CI-Server. Der Grund sind meist Unterschiede in der Umgebung: Datenbankversion, Redis-Einstellungen, fehlende Testdienste. Integrationstests mit C# und Docker lösen dieses Problem ein für alle Mal. In diesem Artikel sehen wir uns an, wie man eine isolierte Testumgebung mit Containern einrichtet, echte Integrationstests schreibt und sie in eine CI/CD-Pipeline integriert.



Warum Docker das perfekte Werkzeug für C#-Integrationstests ist



Integrationstests prüfen die Interaktion zwischen Systemkomponenten: Anwendung + Datenbank, API + Cache, Microservices untereinander. Ohne Docker müsste man:

  • PostgreSQL, SQL Server, Redis auf jedem Entwicklerrechner installieren
  • Den CI-Server mit denselben Versionen konfigurieren
  • Daten zwischen Testläufen bereinigen

Docker löst all diese Aufgaben. Sie beschreiben die Infrastruktur als Code (Dockerfile, docker-compose.yml), und sie wird auf jedem System identisch bereitgestellt. Für C# (.NET) ist dies besonders relevant, da Microsoft Containerisierung aktiv unterstützt: offizielle .NET SDK-Images, SQL Server in einem Linux-Container, Integration mit Azure DevOps.



Projekt-Setup: Testcontainers + xUnit



Für Integrationstests mit C# und Docker verwenden wir die Bibliothek Testcontainers (verfügbar auf NuGet). Sie verwaltet den Container-Lebenszyklus automatisch: startet vor Tests, stoppt nach Abschluss.



Schritt 1. Pakete installieren



Erstellen Sie ein neues Projekt für Tests oder öffnen Sie ein bestehendes. Fügen Sie NuGet-Pakete hinzu:

dotnet add package Testcontainersdotnet add package Testcontainers.PostgreSqldotnet add package xunitdotnet add package FluentAssertions


Schritt 2. Basisklasse für Tests mit Containern



Erstellen wir eine abstrakte Klasse, die PostgreSQL vor dem Ausführen der Tests startet:

using Testcontainers.PostgreSql;using Xunit;

public abstract class IntegrationTestBase : IAsyncLifetime{ private readonly PostgreSqlContainer _postgresContainer;

protected string ConnectionString => _postgresContainer.GetConnectionString();

protected IntegrationTestBase() { _postgresContainer = new PostgreSqlBuilder() .WithImage("postgres:15-alpine") .WithDatabase("testdb") .WithUsername("test") .WithPassword("test123") .Build(); }

public async Task InitializeAsync() { await _postgresContainer.StartAsync(); }

public async Task DisposeAsync() { await _postgresContainer.StopAsync(); }}

Die Klasse implementiert IAsyncLifetime aus xUnit — der Container wird vor dem ersten Test gestartet und nach dem letzten gestoppt. Die Methode GetConnectionString() gibt den dynamischen Port zurück, den Docker dem Container zugewiesen hat.



Schritt 3. Einen Integrationstest schreiben



Schreiben wir nun einen Test, der die Arbeit des Repositorys mit einer echten Datenbank überprüft:

public class UserRepositoryTests : IntegrationTestBase{    [Fact]    public async Task CreateUser_Should_SaveToDatabase()    {        // Arrange        await using var connection = new NpgsqlConnection(ConnectionString);        await connection.OpenAsync();                var repository = new UserRepository(connection);        var user = new User { Name = "Ivan", Email = "ivan@example.com" };

// Act var userId = await repository.CreateAsync(user);

// Assert userId.Should().BeGreaterThan(0); var savedUser = await repository.GetByIdAsync(userId); savedUser.Should().NotBeNull(); savedUser.Name.Should().Be("Ivan"); savedUser.Email.Should().Be("ivan@example.com"); }}

Blogs

Buchempfehlungen