All Articles
Integration Test ใน Go ด้วย testcontainers-go

2025-07-29

Integration Test ใน Go ด้วย testcontainers-go

Read on Medium

Integration Test ใน Go ด้วย testcontainers-go

ทำไมต้อง Integration Test?

Unit test ใช้สำหรับตรวจสอบ function หรือ logic เล็ก ๆ แบบแยกส่วน (isolated) แต่แอปพลิเคชันจริงมักต้องเชื่อมต่อกับระบบอื่น เช่น

  • Database (Postgres, MySQL, MongoDB)
  • Message Queue (Kafka, RabbitMQ)
  • Cache (Redis)
  • หรือแม้แต่ microservices อื่น

การทำ Integration Test จะช่วยให้เรามั่นใจว่าแอปของเราทำงานร่วมกับ service ภายนอกเหล่านี้ได้จริง และไม่มีปัญหาใน runtime

ทำความรู้จัก testcontainers-go

testcontainers-go เป็น Go library ที่ช่วยให้เราสร้าง Docker container ชั่วคราวขึ้นมาในระหว่างรันทดสอบได้ โดยไม่ต้องพึ่ง docker-compose หรือ shell script

จุดเด่นหลัก:

  • เขียนด้วย Go ทั้งหมด (ไม่ต้องสลับไปเขียน shell)
  • ควบคุม lifecycle ของ container ได้
  • ใช้ได้ทั้ง local และบน CI/CD
  • ทดสอบระบบที่ต้องใช้ resource ภายนอกแบบ isolated ได้จริง

เริ่มต้นใช้งาน

ติดตั้ง dependency

go get github.com/testcontainers/testcontainers-go
go get github.com/testcontainers/testcontainers-go/modules/postgres

ตัวอย่าง: Integration Test กับ Postgres

สมมติเรามี repository ที่ใช้ PostgreSQL เราสามารถ spin up container ชั่วคราวระหว่างเทสต์ แล้วเชื่อมต่อเพื่อตรวจสอบได้เลย

package integration_test
import (
"context"
"database/sql"
"testing"
"time"

_ "github.com/lib/pq"
"github.com/testcontainers/testcontainers-go/modules/postgres"
)

func TestWithPostgresContainer(t *testing.T) {
ctx := context.Background()

pgContainer, err := postgres.RunContainer(ctx,
postgres.WithDatabase("testdb"),
postgres.WithUsername("user"),
postgres.WithPassword("pass"),
postgres.WithImage("postgres:14-alpine"),
)
if err != nil {
t.Fatal(err)
}
defer pgContainer.Terminate(ctx)

dsn, err := pgContainer.ConnectionString(ctx, "sslmode=disable")
if err != nil {
t.Fatal(err)
}

db, err := sql.Open("postgres", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()

time.Sleep(2 * time.Second)

var now string
err = db.QueryRow("SELECT NOW()").Scan(&now)
if err != nil {
t.Fatal(err)
}
t.Log("Current time from DB:", now)
}

การ Seed ข้อมูลด้วย Init Script (Optional)

หากต้องการใส่ข้อมูลตั้งต้น เช่น schema หรือ test data สามารถใช้ไฟล์ init.sql ได้

CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT
);INSERT INTO users (name) VALUES ('Alice'), ('Bob'); เพิ่ม script นี้เข้าไปตอนสร้าง container:

postgres.WithInitScripts("init.sql")

ตัวอย่างการทดสอบ Repository

func TestUserRepo(t *testing.T) {
ctx := context.Background()
pg, _ := postgres.RunContainer(ctx, ...)
defer pg.Terminate(ctx)
dsn, _ := pg.ConnectionString(ctx, "sslmode=disable")
db, _ := sql.Open("postgres", dsn)
defer db.Close()

repo := NewUserRepo(db)

err := repo.Create(ctx, "Charlie")
if err != nil {
t.Fatal(err)
}

name, err := repo.GetByID(ctx, 1)
if name != "Charlie" {
t.Fatalf("expected Charlie, got %s", name)
}
}

Best Practices

  • ใช้ defer container.Terminate() เพื่อให้ลบ container หลังเทสต์จบ
  • ใช้ init.sql เท่าที่จำเป็น ทำให้เทสต์เร็วและเข้าใจง่าย
  • ใช้ interface ช่วยให้เปลี่ยนไปใช้ fake/mock ได้ถ้าต้องการ
  • รอ container readiness (เช่น sleep หรือ wait strategy) ป้องกันปัญหา test เร็วเกินไป

ใช้งานบน CI/CD

สามารถรันบน GitHub Actions ได้ทันทีโดยไม่ต้องติดตั้ง Postgres เพิ่ม:

jobs:
integration-test:
runs-on: ubuntu-latest
steps:

  • uses: actions/checkout@v3
  • uses: actions/setup-go@v5
    with:
    go-version: '1.21'
  • run: go mod tidy
  • run: go test ./... -v

เพราะ testcontainers จะสร้างและลบ container ชั่วคราวให้เองระหว่างรันทดสอบ

testcontainers-go ช่วยให้คุณสามารถทำ Integration Test ได้อย่างเป็นระบบและมีประสิทธิภาพ มันทำให้การ spin up/tear down ของ service ภายนอกในระหว่างการทดสอบเป็นเรื่องง่าย และเหมาะกับทั้ง local development และ pipeline automation

หากคุณสนใจทำ integration test กับ Redis, Kafka หรือ MongoDB ก็สามารถต่อยอดจากแนวคิดเดียวกันนี้ได้เช่นกัน และถ้าคุณใช้ gorm, sqlx, หรือ pgx ก็สามารถเชื่อมต่อกับ container ที่สร้างจาก testcontainers-go ได้โดยตรง

© 2026 Nanpipat Klinpratoom. All Rights Reserved.