All Go templates

Go Testing Rules

Table-driven tests, subtests, testify, integration tests, coverage.

DevZone Tools1,170 copiesUpdated Apr 16, 2026Go
# CLAUDE.md — Go Testing Rules

## Layout

- Test files live next to the code: `foo.go` + `foo_test.go`.
- One package per directory. Use `package foo_test` (external test) when you only want to test the public API.
- Helpers live in `*_test.go` files; they're not exported to consumers.

## Table-driven tests

```go
func TestParse(t *testing.T) {
    cases := []struct {
        name    string
        input   string
        want    Result
        wantErr bool
    }{
        {"empty", "", Result{}, true},
        {"simple", "a=1", Result{A: 1}, false},
    }
    for _, tc := range cases {
        t.Run(tc.name, func(t *testing.T) {
            got, err := Parse(tc.input)
            if (err != nil) != tc.wantErr {
                t.Fatalf("err = %v, wantErr %v", err, tc.wantErr)
            }
            if got != tc.want {
                t.Errorf("got %v, want %v", got, tc.want)
            }
        })
    }
}
```

- Subtests via `t.Run` give clean names and let you target one (`go test -run TestParse/simple`).
- `t.Fatalf` for setup failures, `t.Errorf` for assertions where you can keep going.

## Assertions

- Standard library by default. Reach for **testify** (`require`, `assert`) when boilerplate dominates.
- `require.NoError(t, err)` instead of `if err != nil { t.Fatal(err) }` is fine — it's the only `if` you write 100 times.
- Avoid `reflect.DeepEqual` for floats — use a tolerance.

## Test helpers

- Mark helpers with `t.Helper()` so failure lines point to the caller, not the helper.
- Helpers take `*testing.T` first. Cleanup with `t.Cleanup(...)` — runs even if the test fails.

## Integration tests

- Build tag for integration tests: `//go:build integration` at the top of the file. Run with `go test -tags=integration ./...`.
- Spin up real Postgres / Redis with **testcontainers-go** or a local docker-compose.
- Reset state between tests — truncate tables in `t.Cleanup`.

## Concurrency tests

- `-race` in CI. Always. Don't merge without it.
- For ordering, use `testing/synctest` (Go 1.24+) or carefully designed channels — not `time.Sleep`.

## Coverage

- `go test -cover`. Aim for high coverage on services and parsing, not for 100%.
- `-coverprofile=cover.out` + `go tool cover -html=cover.out` to find untested branches.

## Benchmarks

- `func BenchmarkX(b *testing.B)` next to the function.
- Use `b.ReportAllocs()` to surface allocations.
- Compare with `benchstat` after a change. A single run is meaningless.

## Don't

- Don't use `time.Sleep` to "wait for" something async. Use channels, `sync.WaitGroup`, or polling with a deadline.
- Don't share mutable state across `t.Parallel()` tests.
- Don't disable tests with `t.Skip` and leave them. Either fix them or delete them.
- Don't rely on the order of map iteration. It's randomized.

Other Go templates