Integration Testing Go with Docker: A Step-by-Step Guide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Integration Testing Go with Docker: Complete Guide



Integration testing is a key stage in developing reliable Go applications. Unlike unit tests, integration tests verify component interactions: databases, caches, external APIs. Docker allows isolating the test environment, making tests reproducible and independent of the host machine. In this article, we'll explore how to set up integration testing for Go applications with Docker — from simple containers to advanced scenarios with Testcontainers and CI/CD.



1. Why Docker for Go Integration Testing?



Go applications often work with PostgreSQL, Redis, MongoDB, or other services. Manually configuring these dependencies on every developer machine and CI server is a path to errors. Docker solves this problem:

  • Reproducibility — identical environment locally and in CI.
  • Isolation — tests don't clutter the host system.
  • Speed — containers start in seconds.
  • Parallelism — you can run multiple isolated tests simultaneously.


Let's look at a basic example: testing a function that saves data to PostgreSQL.



2. Setting Up Test Environment with Docker Compose



Create a docker-compose.test.yml for the test database:



version: '3.8'services:  postgres_test:    image: postgres:16-alpine    environment:      POSTGRES_USER: test      POSTGRES_PASSWORD: test      POSTGRES_DB: testdb    ports:      - "5433:5432"  # use non-standard port to avoid interfering with local DB    tmpfs: /var/lib/postgresql/data  # data in memory for speed


In the Go test, start the container via os/exec or use the testcontainers-go library. We'll show both approaches.



2.1. Manual Docker Compose Launch from Test



// main_test.gopackage main

import ( "context" "fmt" "os/exec" "testing" "time")

func setupTestDB(t *testing.T) string { t.Helper() // Start the container cmd := exec.Command("docker-compose", "-f", "docker-compose.test.yml", "up", "-d") if err := cmd.Run(); err != nil { t.Fatalf("failed to start docker-compose: %v", err) } // Wait for PostgreSQL readiness 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() // ... test code}


Disadvantages: manual sleep, complexity with parallel tests. Better to use Testcontainers.



2.2. Testcontainers for Go



Install the library:

go get github.com/testcontainers/testcontainers-go


Example with automatic container management:



// 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()

// Request PostgreSQL container 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

Blogs

Book Recommendations