Tests d'intégration Go avec Docker : guide complet
Les tests d'intégration sont une étape clé du développement d'applications Go fiables. Contrairement aux tests unitaires, les tests d'intégration vérifient l'interaction des composants : bases de données, cache, API externes. Docker permet d'isoler l'environnement de test, rendant les tests reproductibles et indépendants de la machine hôte. Dans cet article, nous allons voir comment configurer les tests d'intégration d'applications Go avec Docker — des conteneurs simples aux scénarios avancés avec Testcontainers et CI/CD.
1. Pourquoi Docker pour les tests d'intégration Go ?
Les applications Go fonctionnent souvent avec PostgreSQL, Redis, MongoDB ou d'autres services. La configuration manuelle de ces dépendances sur chaque machine de développement et serveur CI est une source d'erreurs. Docker résout ce problème :
- Reproductibilité — environnement identique en local et dans le CI.
- Isolation — les tests ne polluent pas le système hôte.
- Vitesse — les conteneurs se lancent en quelques secondes.
- Parallélisme — possibilité d'exécuter plusieurs tests isolés simultanément.
Considérons un exemple de base : testons une fonction qui enregistre des données dans PostgreSQL.
2. Configuration de l'environnement de test avec Docker Compose
Créons un docker-compose.test.yml pour la base de données de test :
version: '3.8'services: postgres_test: image: postgres:16-alpine environment: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: testdb ports: - "5433:5432" # utilisation d'un port non standard pour ne pas interférer avec la base locale tmpfs: /var/lib/postgresql/data # données en mémoire pour la rapiditéDans le test Go, on lance le conteneur via os/exec ou on utilise la bibliothèque testcontainers-go. Nous allons présenter les deux approches.
2.1. Lancement manuel de Docker Compose depuis le test
// main_test.gopackage main
import ( "context" "fmt" "os/exec" "testing" "time")
func setupTestDB(t *testing.T) string { t.Helper() // Lancement du conteneur cmd := exec.Command("docker-compose", "-f", "docker-compose.test.yml", "up", "-d") if err := cmd.Run(); err != nil { t.Fatalf("échec du démarrage de docker-compose : %v", err) } // Attente de la disponibilité de PostgreSQL time.Sleep(3 * time.Second) return "postgres://test:test@localhost:5433/testdb?sslmode=disable"}
func TestSaveUser(t *testing.T) { dsn := setupTestDB(t) db, err := sql.Open("postgres", dsn) if err != nil { t.Fatal(err) } defer db.Close() // ... code de test}Inconvénients : sleep manuel, complexité avec les tests parallèles. Il est préférable d'utiliser Testcontainers.
2.2. Testcontainers pour Go
Installez la bibliothèque :
go get github.com/testcontainers/testcontainers-goExemple avec gestion automatique du conteneur :
// repository_test.gopackage repository
import ( "context" "testing" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" "github.com/jackc/pgx/v5/pgxpool")
func TestUserRepository_Save(t *testing.T) { ctx := context.Background()
// Demande d'un conteneur PostgreSQL req := testcontainers.ContainerRequest{ Image: "postgres:16-alpine", ExposedPorts: []string{"5432/tcp"}, Env: map[string]string{ "POSTGRES_USER": "test", "POSTGRES_PASSWORD": "test", "POSTGRES_DB": "testdb", }, WaitingFor: wait.ForLog("database system is ready to accept connections"), }
postgres, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req