Por qué las pruebas E2E son su póliza de seguro
\n\nImagine: ha escrito un microservicio genial en Go. Las pruebas unitarias están en verde, las de integración también. Lo despliega a producción, y… los usuarios se quejan de que el carrito no se vacía después del pago. ¿Le suena familiar? El problema es que las pruebas E2E de aplicaciones Go a menudo quedan para "después". Y es una lástima.
\n\nLas pruebas integrales (End-to-End) son la única forma de verificar que su sistema funciona como un todo unificado: desde la solicitud HTTP hasta la escritura en la base de datos y viceversa. En este artículo no solo hablaremos de teoría, le mostraré ejemplos de código reales en Go que puede copiar y ejecutar hoy mismo.
\n\n¿Qué son las pruebas E2E en el contexto de Golang?
\n\nLas pruebas E2E en Go son la verificación de un escenario de usuario completo: inicio de la aplicación, ejecución de la lógica de negocio, interacción con servicios externos (BD, caché, colas) y verificación del resultado. A diferencia de las pruebas unitarias, no simulamos la base de datos, levantamos una PostgreSQL real en un contenedor.
\n\n¿En qué se diferencian las pruebas E2E de las pruebas de integración?
\n\nA menudo estos conceptos se confunden. Aclaremos las cosas:
\n\n- \n
- Pruebas unitarias — verifican una sola función. Rápidas, aisladas. \n
- Pruebas de integración — verifican la combinación de módulos (por ejemplo, repositorio + BD). \n
- Pruebas E2E — levantan toda la aplicación completa y simulan las acciones de un usuario real. \n
Para las pruebas integrales en Golang utilizaremos el paquete estándar testing junto con potentes utilidades: testcontainers-go y dockertest.
Configuración del entorno: Docker Compose y Testcontainers
\n\nLa forma más sencilla de organizar las pruebas e2e en go es usar Docker. Tiene dos opciones:
\n\nOpción 1: Docker Compose (para CI/CD)
\n\nCree un archivo 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\nOpción 2: Testcontainers (para desarrollo local)
\n\nLa librería testcontainers-go permite levantar contenedores directamente desde las pruebas. Es mi favorita: las pruebas gestionan la infraestructura por sí mismas.
// 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