Integrationstests von Go mit Docker: Vollständige Anleitung
Integrationstests sind ein entscheidender Schritt bei der Entwicklung zuverlässiger Go-Anwendungen. Im Gegensatz zu Unit-Tests prüfen Integrationstests die Interaktion von Komponenten: Datenbanken, Caches, externe APIs. Docker ermöglicht die Isolierung der Testumgebung und macht Tests reproduzierbar und unabhängig vom Host-Rechner. In diesem Artikel zeigen wir, wie man Integrationstests für Go-Anwendungen mit Docker einrichtet – von einfachen Containern bis hin zu fortgeschrittenen Szenarien mit Testcontainers und CI/CD.
1. Warum Docker für Integrationstests von Go?
Go-Anwendungen arbeiten oft mit PostgreSQL, Redis, MongoDB oder anderen Diensten. Die manuelle Einrichtung dieser Abhängigkeiten auf jedem Entwicklerrechner und CI-Server führt zu Fehlern. Docker löst dieses Problem:
- Reproduzierbarkeit – gleiche Umgebung lokal und in CI.
- Isolierung – Tests verschmutzen das Host-System nicht.
- Geschwindigkeit – Container starten in Sekunden.
- Parallelität – mehrere isolierte Tests können gleichzeitig ausgeführt werden.
Betrachten wir ein einfaches Beispiel: Wir testen eine Funktion, die Daten in PostgreSQL speichert.
2. Einrichtung der Testumgebung mit Docker Compose
Erstellen wir docker-compose.test.yml für die Testdatenbank:
version: '3.8'services: postgres_test: image: postgres:16-alpine environment: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: testdb ports: - "5433:5432" # verwenden nicht standardmäßigen Port, um lokale DB nicht zu stören tmpfs: /var/lib/postgresql/data # Daten im Arbeitsspeicher für GeschwindigkeitIm Go-Test starten wir den Container über os/exec oder verwenden die Bibliothek testcontainers-go. Wir zeigen beide Ansätze.
2.1. Manuelles Starten von Docker Compose aus dem Test
// main_test.gopackage main
import ( "context" "fmt" "os/exec" "testing" "time")
func setupTestDB(t *testing.T) string { t.Helper() // Container starten cmd := exec.Command("docker-compose", "-f", "docker-compose.test.yml", "up", "-d") if err := cmd.Run(); err != nil { t.Fatalf("failed to start docker-compose: %v", err) } // Auf PostgreSQL-Bereitschaft warten 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() // ... Testcode}Nachteile: manuelles Sleep, Komplexität bei parallelen Tests. Besser Testcontainers verwenden.
2.2. Testcontainers für Go
Installieren Sie die Bibliothek:
go get github.com/testcontainers/testcontainers-goBeispiel mit automatischer Container-Verwaltung:
// 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()
// PostgreSQL-Container anfordern 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