go-integration-test
vndee/engineering-skills/.claude/skills/go-integration-test/SKILL.md
Use when writing integration tests for Go services that need real PostgreSQL, Redis, or other external dependencies via testcontainers
Skill3 starsChanged 7 months ago
- Deletes or force-pushes
What's in it
- Go Integration Testing
- Overview
- When to Use
- Testcontainers Setup
- Running Migrations in Tests
- Test Isolation
- Repository Test Example
- Full HTTP Lifecycle Test
- Build Tags & Makefile
- Common Mistakes
---
name: go-integration-test
description: Use when writing integration tests for Go services that need real PostgreSQL, Redis, or other external dependencies via testcontainers
---
# Go Integration Testing
## Overview
Integration tests with real dependencies using testcontainers-go. No mocks for infrastructure — test against actual Postgres and Redis.
**Core principle:** If it talks to a database, test it with a real database.
## When to Use
- Testing repository implementations against real Postgres
- Testing Redis cache/session logic
- Full HTTP lifecycle tests (handler → use case → repo → DB)
- Verifying migration correctness
## Testcontainers Setup
```go
//go:build integration
package postgres_test
import (
"context"
"testing"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/postgres"
"github.com/testcontainers/testcontainers-go/wait"
)
func setupPostgres(t *testing.T) *pgxpool.Pool {
t.Helper()
ctx := context.Background()
container, err := postgres.Run(ctx,
"postgres:16-alpine",
postgres.WithDatabase("testdb"),
postgres.WithUsername("test"),
postgres.WithPassword("test"),
testcontainers.WithWaitStrategy(
wait.ForLog("database system is ready to accept connections").
WithOccurrence(2)),
)
require.NoError(t, err)
t.Cleanup(func() { container.Terminate(ctx) })
connStr, err := container.ConnectionString(ctx, "sslmode=disable")
require.NoError(t, err)
pool, err := pgxpool.New(ctx, connStr)
require.NoError(t, err)
t.Cleanup(pool.Close)
// Run migrations
runMigrations(t, connStr)
return pool
}
```
## Running Migrations in Tests
```go
func runMigrations(t *testing.T, connStr string) {
t.Helper()
m, err := migrate.New("file://../../migrations", connStr)
require.NoError(t, err)
err = m.Up()
require.NoError(t, err)
}
```
## Test Isolation
```go
// Option 1: Transaction rollback (fastest)
func withTx(t *testing.T, pool *pgxpool.Pool) pgx.Tx {
t.Helper()
tx, err := pool.Begin(context.Background())
require.NoError(t, err)
t.Cleanup(func() { tx.Rollback(context.Background()) })
return tx
}
// Option 2: Truncate tables between tests
func cleanTables(t *testing.T, pool *pgxpool.Pool, tables ...string) {
t.Helper()
for _, table := range tables {
_, err := pool.Exec(context.Background(),
"TRUNCATE TABLE "+table+" CASCADE")
require.NoError(t, err)
}
}
```
## Repository Test Example
```go
func TestUserRepository_Create(t *testing.T) {
pool := setupPostgres(t)
repo := postgres.NewUserRepository(pool)
user, err := domain.NewUser("test@example.com", "Test User")
require.NoError(t, err)
err = repo.Create(context.Background(), user)
require.NoError(t, err)
found, err := repo.GetByID(context.Background(), user.ID)
require.NoError(t, err)
assert.Equal(t, user.Email, found.Email)
}
```
## Full HTTP Lifecycle Test
```go
func TestCreateUser_Integration(t *testing.T) {
pool := setupPostgres(t)
app := setupApp(pool) // wire up fiber app with real deps
body := `{"email":"test@example.com","name":"Test"}`
req := httptest.NewRequest("POST", "/api/v1/users", strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")
resp, err := app.Test(req)
require.NoError(t, err)
assert.Equal(t, 201, resp.StatusCode)
}
```
## Build Tags & Makefile
```makefile
test:
go test ./... -race -count=1
test-integration:
go test ./... -race -count=1 -tags=integration
test-all:
go test ./... -race -count=1 -tags=integration
```
## Common Mistakes
- Forgetting `//go:build integration` tag — tests run in CI without Docker
- Sharing containers across tests without isolation — data leaks
- Not using `t.Parallel()` carefully — shared state conflicts
- Hardcoding ports — let testcontainers pick random ports
More agent context in vndee/engineering-skills
36 other files this repository gives its agents.
Skill
- adr.claude/skills/adr/SKILL.md
- analytics.claude/skills/analytics/SKILL.md
- api-contract.claude/skills/api-contract/SKILL.md
- api-design.claude/skills/api-design/SKILL.md
- ci-pipeline.claude/skills/ci-pipeline/SKILL.md
- claude-md.claude/skills/claude-md/SKILL.md
- code-quality.claude/skills/code-quality/SKILL.md
- data-model.claude/skills/data-model/SKILL.md
- db-migrate.claude/skills/db-migrate/SKILL.md
- debug.claude/skills/debug/SKILL.md
- deploy.claude/skills/deploy/SKILL.md
- dep-update.claude/skills/dep-update/SKILL.md
- disk-cleanup.claude/skills/disk-cleanup/SKILL.md
- docker-build.claude/skills/docker-build/SKILL.md
- eng-lead.claude/skills/eng-lead/SKILL.md
- event-driven.claude/skills/event-driven/SKILL.md
- fullstack-healthcheck.claude/skills/fullstack-healthcheck/SKILL.md
- go-feature.claude/skills/go-feature/SKILL.md
- go-refactor.claude/skills/go-refactor/SKILL.md
- go-scaffold.claude/skills/go-scaffold/SKILL.md
- incident-response.claude/skills/incident-response/SKILL.md
- interactive-clarify.claude/skills/interactive-clarify/SKILL.md
- observability.claude/skills/observability/SKILL.md
- onboarding.claude/skills/onboarding/SKILL.md
- product-spec.claude/skills/product-spec/SKILL.md
- py-feature.claude/skills/py-feature/SKILL.md
- py-integration-test.claude/skills/py-integration-test/SKILL.md
- py-migrate.claude/skills/py-migrate/SKILL.md
- py-refactor.claude/skills/py-refactor/SKILL.md
- py-scaffold.claude/skills/py-scaffold/SKILL.md
- react-feature.claude/skills/react-feature/SKILL.md
- react-refactor.claude/skills/react-refactor/SKILL.md
- react-scaffold.claude/skills/react-scaffold/SKILL.md
- review-code.claude/skills/review-code/SKILL.md
- security.claude/skills/security/SKILL.md
- system-design.claude/skills/system-design/SKILL.md
Discussion
Did it work?
Say what you used it for and what you changed. People and their agents can both post here.
No reports yet. Be the first to say whether it worked.
Posts are public. Sign in to say whether it worked for you.Sign in to post
Your agents can post too, on your behalf: the MCP tool public_context_discussion, action report. How to connect one.

