Testes de Integração Go com Docker: Nível Avançado
Testes de integração são uma etapa criticamente importante no desenvolvimento de microsserviços confiáveis em Go. Testes unitários isolados não garantem que sua aplicação interaja corretamente com dependências externas: bancos de dados, filas de mensagens, caches e APIs de terceiros. O Docker permite levantar instâncias reais desses serviços no ambiente de teste, garantindo a máxima confiabilidade das verificações. Neste artigo, abordaremos técnicas avançadas de testes de integração de aplicações Go usando Docker.
Por que o Docker é importante para testes de integração em Go?
A abordagem tradicional com mocks e stubs verifica apenas a lógica do seu código, mas não o comportamento em conjunto com um SGBD ou broker real. O Docker resolve esse problema:
- Reprodutibilidade do ambiente: os testes são executados em condições idênticas localmente e no CI/CD.
- Isolamento: cada conjunto de testes pode ter sua própria instância do serviço.
- Sem "mágica": você testa exatamente o driver/cliente que usa em produção.
No entanto, o gerenciamento manual de contêineres via os/exec ou scripts shell rapidamente se torna instável. Bibliotecas como testcontainers-go vêm em socorro.
Biblioteca Testcontainers para Go: instalação e exemplo básico
testcontainers-go é um wrapper Go que gerencia programaticamente o ciclo de vida de contêineres Docker. Ele baixa automaticamente as imagens, inicia, aguarda a prontidão e limpa os contêineres após o teste.
Instalação
go get github.com/testcontainers/testcontainers-gogo get github.com/testcontainers/testcontainers-go/modules/postgresExemplo básico: teste com 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. Inicia o contêiner 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) // Limpeza automática
// 2. Obtém a string de conexão connStr, err := pgContainer.ConnectionString(ctx, "sslmode=disable") if err != nil { t.Fatal(err) }
// 3. Conecta e executa uma consulta db, err := sql.Open("postgres", connStr) if err != nil { t.Fatal(err) } defer db.Close()
if err := db.Ping(); err != nil { t.Fatal("Não foi possível conectar ao banco de dados:", err) }
// Verifica se podemos criar uma tabela e inserir dados _, err = db.Exec(`CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name TEXT)`) if err != nil { t.Fatal(err) } t.Log("Teste de integração com PostgreSQL concluído com sucesso!") }Técnicas avançadas: execução paralela e esperas personalizadas
Quando há muitos testes, a execução sequencial de contêineres para cada teste retarda a execução. Usamos paralelismo Go e contêineres compartilhados.
Testes paralelos com contêiner compartilhado
Criamos um contêiner para todo o pacote via TestMain:
package integrat