| name | go-testing |
| description | Write clear, fast, deterministic Go tests including mandatory repo integration tests (x/testkit) and app unit tests. Use when adding or fixing Go tests, implementing internal/repo or internal/app, designing table-driven tests, writing benchmarks, using testify, or improving coverage in this workspace. |
Go Testing
Tests are documentation that runs. Make them readable, deterministic, and fast.
Repo integration tests (mandatory — enforced by verify.sh)
Every method on every repository port in internal/repo/port.go must have an integration test in
internal/repo/integration_test/ using x/testkit against real Gnomock Postgres/Redis — not mocks.
Per method, cover:
- Happy path — creates/reads/updates as intended
- Not found — missing row / empty result
- Conflict — unique violation or constraint error
Read MCP architecture/integration-tests. Scaffold provides setup_test.go; add *_test.go files per domain. Run
go test ./internal/repo/integration_test/... and confirm green before treating the repo as done.
App unit tests (mandatory — enforced by verify.sh)
Every exported method on internal/app.App needs a unit test in internal/app/*_test.go. Test business rules with injected fakes/mocks
at the port boundary — not integration tests.
When to use
- Adding tests for new Go behavior or covering a bug fix.
- Designing table-driven tests, subtests, fuzz tests, or benchmarks.
- Diagnosing flaky, slow, or order-dependent tests.
Conventions
- Table-driven + subtests. One
t.Run(tc.name, ...) per case so failures
point at the exact scenario.
- Arrange / Act / Assert. Keep the three sections visually distinct.
- Deterministic. No real clocks, randomness, network, or sleeps. Inject
time/IDs; use
context.Context with deadlines.
- Parallel where safe.
t.Parallel() for independent cases; capture loop
variables (not needed in Go 1.22+, but be explicit if unsure).
- Helpers call
t.Helper() so failures report the caller's line.
- Golden files for large outputs; gate updates behind a
-update flag.
Framework
Use standard testing with github.com/stretchr/testify (assert /
require). Prefer table-driven tests and t.Run subtests. Match surrounding
package style; do not introduce alternate assertion libraries.
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestParse(t *testing.T) {
tests := []struct {
name string
in string
want int
wantErr bool
}{
{name: "valid", in: "42", want: 42},
{name: "empty", in: "", wantErr: true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
got, err := Parse(tc.in)
if tc.wantErr {
assert.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tc.want, got)
})
}
}
Benchmarks & fuzzing
func BenchmarkX(b *testing.B) with b.ResetTimer() after setup and
b.ReportAllocs() when allocations matter.
- Add a
func FuzzX(f *testing.F) with seed corpus for parsers and decoders.
Commands
cd <module>
go test ./... -race
go test ./... -run TestName -v
go test ./... -bench . -benchmem
Checklist
- Test names describe behavior, not implementation.
- Failures print expected vs got with enough context to debug.
- New behavior and every fixed bug has a covering test.
- Every repo port method has a passing
x/testkit integration test.
- Every exported
App method has a unit test.
make verify passes before reporting backend work done.