Testes de Integração Go com Docker: Guia Completo
Testes de integração são uma etapa fundamental no desenvolvimento de aplicações Go confiáveis. Diferente dos testes unitários, os testes de integração verificam a interação entre componentes: banco de dados, cache, APIs externas. O Docker permite isolar o ambiente de teste, tornando os testes reproduzíveis e independentes da máquina host. Neste artigo, vamos abordar como configurar testes de integração de aplicações Go com Docker — desde contêineres simples até cenários avançados com Testcontainers e CI/CD.
1. Por que Docker para testes de integração Go?
Aplicações Go frequentemente trabalham com PostgreSQL, Redis, MongoDB ou outros serviços. Configurar manualmente essas dependências em cada máquina do desenvolvedor e servidor CI é um caminho para erros. O Docker resolve esse problema:
- Reprodutibilidade — mesmo ambiente localmente e no CI.
- Isolamento — testes não poluem o sistema host.
- Velocidade — contêineres iniciam em segundos.
- Paralelismo — é possível executar vários testes isolados simultaneamente.
Vamos considerar um exemplo básico: testar uma função que salva dados no PostgreSQL.
2. Configuração do ambiente de teste com Docker Compose
Criaremos um docker-compose.test.yml para o banco de dados de teste:
version: '3.8'services: postgres_test: image: postgres:16-alpine environment: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: testdb ports: - "5433:5432" # usamos porta não padrão para não interferir com o BD local tmpfs: /var/lib/postgresql/data # dados em memória para velocidadeNo teste Go, iniciamos o contêiner via os/exec ou usamos a biblioteca testcontainers-go. Mostraremos ambas as abordagens.
2.1. Inicialização manual do Docker Compose a partir do teste
// main_test.gopackage main
import ( "context" "fmt" "os/exec" "testing" "time")
func setupTestDB(t *testing.T) string { t.Helper() // Inicia o contêiner cmd := exec.Command("docker-compose", "-f", "docker-compose.test.yml", "up", "-d") if err := cmd.Run(); err != nil { t.Fatalf("falha ao iniciar docker-compose: %v", err) } // Aguarda o PostgreSQL ficar pronto 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() // ... código do teste}Desvantagens: sleep manual, complexidade com testes paralelos. É melhor usar Testcontainers.
2.2. Testcontainers para Go
Instale a biblioteca:
go get github.com/testcontainers/testcontainers-goExemplo com gerenciamento automático do contêiner:
// 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()
// Solicita um contêiner 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