Tests d'intégration C# avec Docker : Comment se débarrasser des problèmes d'environnement
Chaque développeur C# a déjà vécu cette situation : les tests passent sur la machine locale mais échouent sur le serveur CI. La raison est le plus souvent due à des différences d'environnement : version de base de données, paramètres Redis, services de test manquants. Les tests d'intégration C# avec Docker résolvent ce problème une fois pour toutes. Dans cet article, nous verrons comment configurer un environnement de test isolé à l'aide de conteneurs, écrire de véritables tests d'intégration et les intégrer dans un pipeline CI/CD.
Pourquoi Docker est l'outil parfait pour les tests d'intégration C#
Les tests d'intégration vérifient l'interaction entre les composants du système : application + base de données, API + cache, microservices entre eux. Sans Docker, vous devriez :
- Installer PostgreSQL, SQL Server, Redis sur chaque machine de développeur
- Configurer le serveur CI avec les mêmes versions
Docker résout toutes ces tâches. Vous décrivez l'infrastructure dans le code (Dockerfile, docker-compose.yml), et elle se déploie à l'identique sur n'importe quel système. Pour C# (.NET), c'est particulièrement pertinent car Microsoft soutient activement la conteneurisation : images officielles du SDK .NET, SQL Server dans un conteneur Linux, intégration avec Azure DevOps.
Configuration du projet : Testcontainers + xUnit
Pour les tests d'intégration C# avec Docker, nous utiliserons la bibliothèque Testcontainers (disponible sur NuGet). Elle gère automatiquement le cycle de vie des conteneurs : démarrage avant les tests, arrêt après la fin.
Étape 1. Installation des packages
Créez un nouveau projet pour les tests ou ouvrez-en un existant. Ajoutez les packages NuGet :
dotnet add package Testcontainersdotnet add package Testcontainers.PostgreSqldotnet add package xunitdotnet add package FluentAssertionsÉtape 2. Classe de base pour les tests avec conteneurs
Créons une classe abstraite qui lancera PostgreSQL avant d'exécuter les tests :
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(); }}La classe implémente IAsyncLifetime de xUnit — le conteneur démarrera avant le premier test et s'arrêtera après le dernier. La méthode GetConnectionString() retourne le port dynamique que Docker a attribué au conteneur.
Étape 3. Écrire un test d'intégration
Écrivons maintenant un test qui vérifie le travail du dépôt avec une base de données réelle :
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.Sho