Pourquoi les tests E2E sont votre police d'assurance
\n\nImaginez : vous avez écrit un super microservice en Go. Les tests unitaires sont verts, les tests d'intégration aussi. Vous déployez en production, et… les utilisateurs se plaignent que le panier ne se vide pas après le paiement. Cela vous dit quelque chose ? Le problème est que les tests E2E d'applications Go sont souvent remis à plus tard. Et c'est dommage.
\n\nLes tests de bout en bout (End-to-End) sont le seul moyen de vérifier que votre système fonctionne comme un tout cohérent : de la requête HTTP à l'écriture dans la base de données et inversement. Dans cet article, nous ne nous contenterons pas de parler de théorie — je vais vous montrer des exemples de code réels en Go que vous pouvez copier et exécuter dès aujourd'hui.
\n\nQu'est-ce que le test E2E dans le contexte de Golang ?
\n\nLe test E2E Go est la vérification d'un scénario utilisateur complet : démarrage de l'application, exécution de la logique métier, interaction avec les services externes (BDD, cache, files d'attente) et vérification du résultat. Contrairement aux tests unitaires, nous ne mockons pas la base de données — nous lançons une vraie PostgreSQL dans un conteneur.
\n\nEn quoi les tests E2E diffèrent-ils des tests d'intégration ?
\n\nCes concepts sont souvent confondus. Mettons les points sur les i :
\n\n- \n
- Tests unitaires — testent une seule fonction. Rapides, isolés. \n
- Tests d'intégration — testent l'interaction de plusieurs modules (par exemple, dépôt + BDD). \n
- Tests E2E — lancent l'application entière et simulent les actions d'un utilisateur réel. \n
Pour les tests de bout en bout Golang, nous utiliserons le package standard testing ainsi que des utilitaires puissants : testcontainers-go et dockertest.
Configuration de l'environnement : Docker Compose et Testcontainers
\n\nLa façon la plus simple d'organiser les tests e2e go est d'utiliser Docker. Vous avez deux options :
\n\nOption 1 : Docker Compose (pour CI/CD)
\n\nCréez un fichier docker-compose.test.yml :
version: '3.8'\nservices:\n db:\n image: postgres:15-alpine\n environment:\n POSTGRES_DB: testdb\n POSTGRES_USER: test\n POSTGRES_PASSWORD: test\n ports:\n - "5432:5432"\n \n app:\n build: .\n depends_on:\n - db\n environment:\n DATABASE_URL: "postgres://test:test@db:5432/testdb?sslmode=disable"\n ports:\n - "8080:8080"\n\n\nOption 2 : Testcontainers (pour le développement local)
\n\nLa bibliothèque testcontainers-go permet de lancer des conteneurs directement depuis les tests. C'est mon option préférée — les tests gèrent eux-mêmes l'infrastructure.
// setup_test.go\npackage e2e_test\n\nimport (\n "context"\n "testing"\n "time"\n\n "github.com/testcontainers/testcontainers-go"\n "github.com/testcontainers/testcontainers-go/wait"\n)\n\nfunc setupPostgres(t *testing.T) string {\n t.Helper()\n \n ctx := context.Background()\n \n req := testcontainers.ContainerRequest{\n Image: "postgres:15-alpine",\n ExposedPorts: []string{"5432/tcp"},\n Env: map[string]string{\n "POSTGRES_DB": "testdb",\n "POSTGRES_USER": "test",\n "POSTGRES_PASSWORD": "test",\n },\n WaitingFor: wait.ForLog("database system is ready to accept connections").\n WithStartupTimeout(30 * time.Second),\n }\n \n container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{\n ContainerRequest: req,\n Started: true,\n })\n if err != nil {\n t.Fatalf("failed to start container: %v", err)\n }\n \n t.Cleanup(func() {\n container.Terminate(ctx)\n })\n \n host, _ := container.Host(ctx)\n port, _ := container.MappedPort(ctx, "5432")\n \n return "postgres://test:test@" + host + ":" + port.Port() + "/testdb?sslmode=disable"\n}\n\n\n