Go Testing
Write tests that constrain observable behavior and produce useful failures. Match the repository's existing testing style and dependencies before introducing a new framework or generator.
Start from the Contract
- Read the implementation, callers, public documentation, existing tests, and nearby helpers.
- Identify inputs, outputs, side effects, error identity, ordering, timing, concurrency, and ownership that callers can observe.
- Choose the smallest test level that exercises the contract reliably.
- Reproduce a bug with a failing test before changing production code when practical.
- Avoid asserting incidental implementation details unless they are the contract under review.
Choose a Test Shape
- Use a direct test for one behavior with a short setup.
- Use table-driven subtests when cases share the same arrange/act/assert structure and case names make failures clearer. Do not force unrelated scenarios into a large table.
- Use examples when executable documentation and rendered output are the primary value.
- Use fuzzing for parsers, codecs, state transitions, and input spaces with useful invariants. Seed important regressions.
- Use integration tests when the contract depends on a real database, filesystem, network protocol, process, or service behavior that a unit test cannot establish.
- Use benchmarks only for performance questions; functional tests should not encode fragile timing budgets.
Read table, fuzz, and example recipes when choosing concrete t.Run, f.Add/f.Fuzz, or Example shapes. Keep a table only while all cases share one readable assertion path.
Assertions and Failures
- Prefer messages that include the operation, input or case, actual value, and expected value.
- Use
t.Fatalf or a fatal helper only when later assertions cannot run meaningfully.
- Use
errors.Is or errors.As when error identity or type is the contract. Avoid matching full error strings unless text itself is public behavior.
- Mark helpers with
t.Helper() and register cleanup with t.Cleanup() when the test owns a resource.
- Bind assertion helpers to the current subtest's
*testing.T; do not reuse a helper that captured the parent test.
- Compare structured values in a way that reports useful differences and respects semantic equality such as time instants, nil versus empty collections, or unordered results.
Isolation and Parallelism
Each test should establish and clean up its own state. Use t.TempDir(), ephemeral listeners, unique database namespaces, and injected clocks or dependencies where appropriate.
Use t.Parallel() only after checking all shared state, environment variables, current working directory, ports, fixtures, global registries, and mutable package variables. Remember that a parallel subtest pauses until its parent returns; arrange parent-owned resources and cleanup accordingly. Run with shuffling or repetition when investigating order dependence.
For tests that interact with goroutines, synchronize on events rather than sleeps. Use the race detector and the toolchain's supported deterministic time/concurrency facilities when they fit the module target. A test timeout should bound the whole command or operation; avoid timeout helpers that leave runaway goroutines behind. See references/helpers.md.
Test Doubles
Prefer a small fake, stub, or function value that implements the consumer's actual dependency boundary. Mock call expectations only when the interaction sequence is itself important. Do not create production interfaces solely to satisfy a mocking framework when a simpler seam is available.
Read references/mocking.md when choosing among fakes, stubs, mocks, and injected functions.
HTTP and Integration Tests
- Read references/http-testing.md for handler, client, and server tests using
net/http/httptest.
- Read references/integration-testing.md when tests require external infrastructure, schemas, migrations, build tags, or environment-dependent setup.
Coverage, Race Detection, and Repetition
Coverage shows executed statements, not whether assertions are meaningful or every branch is tested. Use it to locate unexercised risk, not as the goal of the suite.
Typical commands, adjusted to the repository:
go test ./...
go test -race ./...
go test -shuffle=on -count=1 ./...
go test -run 'TestName/subtest' -count=1 ./path/to/pkg
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out
Use repetition to reproduce flakes, but keep the exact failing seed, shuffle value, environment, and command. A passing repeated run reduces suspicion; it does not prove the absence of a race.
Review Checklist
- Does each test fail for the intended regression?
- Are important success, boundary, and error paths covered without duplicating implementation logic?
- Are assertions attributed to the correct subtest?
- Can the test run alone, in any order, and under the race detector when relevant?
- Are cleanup and resource ownership explicit?
- Are time, randomness, network, filesystem, and external-service dependencies controlled?
- Does the test use APIs available to the module's Go target?
- Did the change add a dependency or fixture system without a demonstrated need?
1---2name: golang-testing3description: Write, review, or debug Go tests using the standard testing package, table cases, subtests, parallel tests, fuzzing, race checks, integration isolation, HTTP utilities, examples, fixtures, and test doubles. Use when test design, reliability, or behavior is central.4license: MIT5---67# Go Testing89Write tests that constrain observable behavior and produce useful failures. Match the repository's existing testing style and dependencies before introducing a new framework or generator.1011## Start from the Contract12131. Read the implementation, callers, public documentation, existing tests, and nearby helpers.142. Identify inputs, outputs, side effects, error identity, ordering, timing, concurrency, and ownership that callers can observe.153. Choose the smallest test level that exercises the contract reliably.164. Reproduce a bug with a failing test before changing production code when practical.175. Avoid asserting incidental implementation details unless they are the contract under review.1819## Choose a Test Shape2021- Use a direct test for one behavior with a short setup.22- Use table-driven subtests when cases share the same arrange/act/assert structure and case names make failures clearer. Do not force unrelated scenarios into a large table.23- Use examples when executable documentation and rendered output are the primary value.24- Use fuzzing for parsers, codecs, state transitions, and input spaces with useful invariants. Seed important regressions.25- Use integration tests when the contract depends on a real database, filesystem, network protocol, process, or service behavior that a unit test cannot establish.26- Use benchmarks only for performance questions; functional tests should not encode fragile timing budgets.2728Read [table, fuzz, and example recipes](references/table-fuzz-examples.md) when choosing concrete `t.Run`, `f.Add`/`f.Fuzz`, or `Example` shapes. Keep a table only while all cases share one readable assertion path.2930## Assertions and Failures3132- Prefer messages that include the operation, input or case, actual value, and expected value.33- Use `t.Fatalf` or a fatal helper only when later assertions cannot run meaningfully.34- Use `errors.Is` or `errors.As` when error identity or type is the contract. Avoid matching full error strings unless text itself is public behavior.35- Mark helpers with `t.Helper()` and register cleanup with `t.Cleanup()` when the test owns a resource.36- Bind assertion helpers to the current subtest's `*testing.T`; do not reuse a helper that captured the parent test.37- Compare structured values in a way that reports useful differences and respects semantic equality such as time instants, nil versus empty collections, or unordered results.3839## Isolation and Parallelism4041Each test should establish and clean up its own state. Use `t.TempDir()`, ephemeral listeners, unique database namespaces, and injected clocks or dependencies where appropriate.4243Use `t.Parallel()` only after checking all shared state, environment variables, current working directory, ports, fixtures, global registries, and mutable package variables. Remember that a parallel subtest pauses until its parent returns; arrange parent-owned resources and cleanup accordingly. Run with shuffling or repetition when investigating order dependence.4445For tests that interact with goroutines, synchronize on events rather than sleeps. Use the race detector and the toolchain's supported deterministic time/concurrency facilities when they fit the module target. A test timeout should bound the whole command or operation; avoid timeout helpers that leave runaway goroutines behind. See [references/helpers.md](references/helpers.md).4647## Test Doubles4849Prefer a small fake, stub, or function value that implements the consumer's actual dependency boundary. Mock call expectations only when the interaction sequence is itself important. Do not create production interfaces solely to satisfy a mocking framework when a simpler seam is available.5051Read [references/mocking.md](references/mocking.md) when choosing among fakes, stubs, mocks, and injected functions.5253## HTTP and Integration Tests5455- Read [references/http-testing.md](references/http-testing.md) for handler, client, and server tests using `net/http/httptest`.56- Read [references/integration-testing.md](references/integration-testing.md) when tests require external infrastructure, schemas, migrations, build tags, or environment-dependent setup.5758## Coverage, Race Detection, and Repetition5960Coverage shows executed statements, not whether assertions are meaningful or every branch is tested. Use it to locate unexercised risk, not as the goal of the suite.6162Typical commands, adjusted to the repository:6364```bash65go test ./...66go test -race ./...67go test -shuffle=on -count=1 ./...68go test -run 'TestName/subtest' -count=1 ./path/to/pkg69go test -coverprofile=coverage.out ./...70go tool cover -func=coverage.out71```7273Use repetition to reproduce flakes, but keep the exact failing seed, shuffle value, environment, and command. A passing repeated run reduces suspicion; it does not prove the absence of a race.7475## Review Checklist7677- Does each test fail for the intended regression?78- Are important success, boundary, and error paths covered without duplicating implementation logic?79- Are assertions attributed to the correct subtest?80- Can the test run alone, in any order, and under the race detector when relevant?81- Are cleanup and resource ownership explicit?82- Are time, randomness, network, filesystem, and external-service dependencies controlled?83- Does the test use APIs available to the module's Go target?84- Did the change add a dependency or fixture system without a demonstrated need?