Tests d'intégration Go avec Docker : niveau avancé
Les tests d'intégration sont une étape cruciale dans le développement de microservices fiables en Go. Les tests unitaires isolés ne garantissent pas que votre application interagit correctement avec les dépendances externes : bases de données, files d'attente de messages, caches et API tierces. Docker permet de lancer des instances réelles de ces services dans un environnement de test, assurant une fiabilité maximale des vérifications. Dans cet article, nous aborderons les techniques avancées de tests d'intégration d'applications Go utilisant Docker.
Pourquoi Docker est-il important pour les tests d'intégration en Go ?
L'approche traditionnelle avec des mocks et des stubs ne vérifie que la logique de votre code, mais pas le comportement en liaison avec un vrai SGBD ou un broker. Docker résout ce problème :
- Reproductibilité de l'environnement : les tests s'exécutent dans des conditions identiques en local et dans le CI/CD.
- Isolation : chaque ensemble de tests peut avoir sa propre instance de service.
- Pas de "magie" : vous testez exactement le pilote/client que vous utilisez en production.
Cependant, la gestion manuelle des conteneurs via os/exec ou des scripts shell devient rapidement instable. Des bibliothèques comme testcontainers-go viennent à la rescousse.
Bibliothèque Testcontainers pour Go : installation et exemple de base
testcontainers-go est un wrapper Go qui gère programmatiquement le cycle de vie des conteneurs Docker. Il télécharge automatiquement les images, les lance, attend leur disponibilité et nettoie les conteneurs après le test.
Installation
go get github.com/testcontainers/testcontainers-gogo get github.com/testcontainers/testcontainers-go/modules/postgresExemple de base : test avec PostgreSQL
package integration
import ( "context" "database/sql" "testing" "time"
"github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/postgres" "github.com/testcontainers/testcontainers-go/wait" _ "github.com/lib/pq")
func TestPostgresConnection(t *testing.T) { ctx := context.Background()
// 1. Lancement du conteneur PostgreSQL pgContainer, err := postgres.RunContainer(ctx, testcontainers.WithImage("postgres:15"), postgres.WithDatabase("testdb"), postgres.WithUsername("user"), postgres.WithPassword("password"), testcontainers.WithWaitStrategy( wait.ForLog("database system is ready to accept connections"). WithOccurrence(2). WithStartupTimeout(30*time.Second)), ) if err != nil { t.Fatal(err) } defer pgContainer.Terminate(ctx) // Nettoyage automatique
// 2. Obtention de la chaîne de connexion connStr, err := pgContainer.ConnectionString(ctx, "sslmode=disable") if err != nil { t.Fatal(err) }
// 3. Connexion et exécution de la requête db, err := sql.Open("postgres", connStr) if err != nil { t.Fatal(err) } defer db.Close()
if err := db.Ping(); err != nil { t.Fatal("Impossible de se connecter à la BD :", err) }
// Vérification que nous pouvons créer une table et insérer des données _, err = db.Exec(`CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name TEXT)`) if err != nil { t.Fatal(err) } t.Log("Test d'intégration avec PostgreSQL réussi !")}Techniques avancées : exécution parallèle et attentes personnalisées
Lorsque les tests sont nombreux, le lancement séquentiel des conteneurs pour chaque test ralentit l'exécution. Utilisons le parallélisme de Go et les conteneurs partagés.
Tests parallèles avec un conteneur partagé
Création d'un seul conteneur pour tout le package via TestMain :
package integrat