Why E2E Testing is Your Insurance Policy
\n\nImagine: you've written a cool microservice in Go. Unit tests are green, integration tests too. You deploy to production, and... users complain that the cart doesn't clear after payment. Sound familiar? The problem is that E2E testing Go applications often gets left "for later." And that's a mistake.
\n\nEnd-to-End tests are the only way to verify that your system works as a single unit: from an HTTP request to writing to the database and back. In this article, we won't just talk about theory — I'll show you real Go code examples that you can copy and run today.
\n\nWhat is E2E Testing in the Context of Golang?
\n\nE2E testing Go is checking a complete user scenario: starting the application, executing business logic, interacting with external services (DB, cache, queues), and verifying the result. Unlike unit tests, we don't mock the database — we spin up a real PostgreSQL in a container.
\n\nHow is E2E Different from Integration Tests?
\n\nThese concepts are often confused. Let's set the record straight:
\n\n- \n
- Unit tests — check a single function. Fast, isolated. \n
- Integration tests — check a combination of modules (e.g., repository + DB). \n
- E2E tests — spin up the entire application and simulate real user actions. \n
For end-to-end testing Golang, we will use the standard testing package plus powerful utilities: testcontainers-go and dockertest.
Environment Setup: Docker Compose and Testcontainers
\n\nThe easiest way to organize e2e testing go is to use Docker. You have two paths:
\n\nOption 1: Docker Compose (for CI/CD)
\n\nCreate a docker-compose.test.yml file:
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\nOption 2: Testcontainers (for Local Development)
\n\nThe testcontainers-go library allows you to spin up containers directly from your tests. This is my favorite — tests manage the infrastructure themselves.
// 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