Go repository testing with SQLC integration following hexagonal architecture. Use when writing integration tests for repository layer, testing database operations, and validating domain model persistence. Focuses on testcontainers, table-driven tests, and comprehensive error coverage.
This skill provides guidance on testing repository implementations using real databases with comprehensive error coverage.
assert[ModelName] for EVERY domain model returned by repository methods (e.g., assertCart, assertOrder)assert.Equal on individual model fieldsarrangeState patternsdefer suite.deleteAll() once per test method, never per test casetype repositorySuite struct {
suite.Suite
repo port.Repository
pool *pgxpool.Pool
}
tests := []struct {
name string
buildModel func() domain.Model
arrangeState func(uuid.UUID) error // arrange state for test case before the operation
useModelID func() uuid.UUID // override which model ID to use, if nil use the inserted one
wantError string
}{
// Must test ALL error categories:
{name: "valid input: ok", buildModel: randomModel},
{name: "empty id: error", useModelID: func() uuid.UUID { return uuid.Nil }, wantError: "id is empty"},
{name: "non-existing: not found", useModelID: randomUUID, wantError: "q.Method: record not found"},
{name: "soft-deleted: not found", arrangeState: softDelete, wantError: "q.Method: record not found"},
}
// NEVER do manual field assertions
require.Equal(t, expected.OwnerID, actual.OwnerID) // ❌ FORBIDDEN
require.Len(t, actual.Items, len(expected.Items)) // ❌ FORBIDDEN
// ALWAYS use comprehensive model assertions
assertCart(t, expected, actual) // ✅ REQUIRED
// MUST create for every domain model - compare ALL fields except generated ones
func assertModel(t *testing.T, expected, actual domain.Model) {
opts := cmp.Options{
cmpopts.IgnoreFields(domain.Model{}, "CreatedAt", "UpdatedAt", "ID"),
customComparers,
}
assert.Empty(t, cmp.Diff(expected, actual, opts))
assert.False(t, actual.CreatedAt.IsZero())
}
repository_test package for interface testinggofakeit for realistic random data via helper functionspostgres.WithInitScripts()defer suite.deleteAll() per test method onlySearch for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer