Pruebas de Integración en Go con Docker: Guía Completa
Las pruebas de integración son una etapa clave en el desarrollo de aplicaciones Go confiables. A diferencia de las pruebas unitarias, las pruebas de integración verifican la interacción entre componentes: bases de datos, cachés, APIs externas. Docker permite aislar el entorno de pruebas, haciendo que las pruebas sean reproducibles e independientes de la máquina host. En este artículo, analizaremos cómo configurar las pruebas de integración de aplicaciones Go con Docker, desde contenedores simples hasta escenarios avanzados con Testcontainers y CI/CD.
1. ¿Por qué Docker para pruebas de integración en Go?
Las aplicaciones Go a menudo trabajan con PostgreSQL, Redis, MongoDB u otros servicios. La configuración manual de estas dependencias en cada máquina de desarrollo y servidor CI es un camino hacia los errores. Docker resuelve este problema:
- Reproducibilidad — mismo entorno localmente y en CI.
- Aislamiento — las pruebas no contaminan el sistema host.
- Velocidad — los contenedores se inician en segundos.
- Paralelismo — se pueden ejecutar múltiples pruebas aisladas simultáneamente.
Consideremos un ejemplo básico: probamos una función que guarda datos en PostgreSQL.
2. Configuración del entorno de pruebas con Docker Compose
Creemos un docker-compose.test.yml para la base de datos de prueba:
version: '3.8'services: postgres_test: image: postgres:16-alpine environment: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: testdb ports: - "5433:5432" # usamos un puerto no estándar para no interferir con la BD local tmpfs: /var/lib/postgresql/data # datos en memoria para velocidadEn la prueba de Go, levantamos el contenedor mediante os/exec o usamos la biblioteca testcontainers-go. Mostraremos ambos enfoques.
2.1. Inicio manual de Docker Compose desde la prueba
// main_test.gopackage main
import ( "context" "fmt" "os/exec" "testing" "time")
func setupTestDB(t *testing.T) string { t.Helper() // Iniciamos el contenedor cmd := exec.Command("docker-compose", "-f", "docker-compose.test.yml", "up", "-d") if err := cmd.Run(); err != nil { t.Fatalf("error al iniciar docker-compose: %v", err) } // Esperamos a que PostgreSQL esté listo 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 de prueba}Desventajas: sleep manual, complejidad con pruebas paralelas. Es mejor usar Testcontainers.
2.2. Testcontainers para Go
Instale la biblioteca:
go get github.com/testcontainers/testcontainers-goEjemplo con gestión automática del contenedor:
// 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()
// Solicitamos un contenedor 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