Go Testing With Testify
Turn a concrete behavior claim into useful Go test evidence. Preserve the
repository's Go version, test framework, and supported execution environment.
Route And Select Activity
- Implement or harden: edit the requested tests and any explicitly covered
production seam. A test request does not automatically authorize an unrelated
production refactor, new dependency, or external service.
- Review: inspect tests and report prioritized findings, evidence, and
consequences. Do not edit files or require remediation to complete a review.
- Diagnose: reproduce the named failure in scope, distinguish a product
defect from a test-apparatus defect, then fix it when the request includes repair.
Use coding-guidance-go for production Go or non-testify tests. Preserve
Ginkgo, Gomega, go-cmp-only, and other intentional stacks. Module bootstrap or
test-tool installation is separate from test authoring.
Add tester-mindset when claims, oracles, or test strategy are still unclear.
Add backend-guidance or backend-systems-guidance only when a service,
repository, queue, or other backend boundary needs that design guidance.
Use security first for a security-led review and add
security-identity-access when its identity scope applies. Routine tests of an
already-defined permission rule can remain here.
Workflow
- Read the scoped tests, implementation,
go.mod, fixtures, and relevant
repository instructions. Identify the Go and testify versions, local test
command, helpers, and available dependencies.
- Name the behavior claim, oracle, and smallest seam that can reveal a defect.
Pure logic needs a unit check; protocol, query, serialization, or lifecycle
behavior may need a real boundary. Passing checks support only their scope.
- Preserve the existing test shape when it is clear. Use table-driven subtests
when cases share setup and assertions; use separate tests when they do not.
Introduce helpers, fakes, mocks, or suites only for a concrete benefit.
- For implementation, exercise the real code and assert the behavior being
claimed. For review, assess these choices without rewriting the tests.
- Validate the narrowest changed test or package, for example
go test ./path/to/pkg -run '^TestName$' -count=1. Add race checks,
repetition, shuffle, or integration runs when the failure mode needs them.
- Inspect failures before changing assertions, retry counts, or timeouts.
Report the exact evidence and remaining uncertainty.
Assertions And Oracles
- Put expected before actual in testify comparisons.
- Use
require for prerequisites whose failure makes later checks unsafe or
meaningless; use assert for independent checks. A fatal failure in a
subtest stops that subtest, not its siblings.
- Call
require.*, t.Fatal, and t.FailNow only on the test goroutine.
Return observations or errors from HTTP handlers and workers through a
synchronized channel or result, then assert on the test goroutine.
- Use
ErrorIs or ErrorAs when error identity or type is the contract.
Error presence alone is sufficient when that is all the contract promises.
Assert text only when it is intended to be stable.
- Use equality, unordered comparison, identity, structural JSON, or tolerances
according to the contract. Exact float equality is valid for exact expected
values; approximate computations need justified tolerances.
- Check fields the behavior promises, including generated IDs, defaults, or
timestamps when relevant. Whole-struct equality is valid when the full value
is the contract; do not discard meaningful fields merely to avoid failures.
- A success-only test can be meaningful. Add separate failure cases where they
protect required behavior; do not require every individual test to exercise
both success and failure.
- Call
t.Helper() in helpers whose failures should identify their caller.
Reject tautologies, mocks that replace the behavior being proved, and assertions
that cannot detect a plausible defect in the stated claim. A no-error assertion
can prove a narrow validation contract but does not prove that a user was stored
or a file's contents are correct. Logs can be the oracle when logging itself is
the requested behavior.
Boundary And Double Choice
Prefer an existing cheap real harness when the risk is at that boundary:
- HTTP protocol behavior:
httptest.NewServer;
- filesystem behavior:
t.TempDir();
- database queries or transactions: a disposable dialect-appropriate harness;
- time or async behavior: explicit synchronization, injected time, or supported
virtual-time testing.
Use a fake for a simple owned collaborator; use testify/mock when argument,
call-count, failure, or ordering expectations improve the test. Respect existing
test seams. Do not introduce wrappers or containers solely to satisfy a generic
mock rule, and do not contact live or metered services without authority.
Wire the double into the subject under test. Assert mock expectations after
owned asynchronous calls finish, using cleanup when it must also run after a
fatal test failure. Keep matchers specific to the promised interaction and
avoid retaining mutable pointer arguments as if they were immutable snapshots.
Concurrency, Time, And Cleanup
- Add
t.Parallel() only after checking shared state, database schemas, ports,
temporary paths, environment, current directory, and goroutine lifetimes.
t.Setenv and t.Chdir cannot be used in parallel tests or with parallel
ancestors; check helper availability against the supported Go version.
- Testify
suite does not support parallel tests. Keep suites when their
scenario and lifecycle organization helps; do not migrate them for style alone.
- Go 1.22+ loop semantics depend on the module language version. Rebind loop
variables where older semantics require it, not as mandatory modern boilerplate.
- Register cleanup next to acquisition. Own cancellation and wait for workers
before test teardown; a deadline limits waiting but does not itself join work.
t.Context() is available from Go 1.24 and cancels before cleanup.
- Prefer completion signals to polling. Use bounded
Eventually only for a
genuinely eventual observable state and synchronize shared reads.
- On Go 1.25+, consider
testing/synctest for compatible in-process concurrent
code. Virtual time is not a replacement for real network or external-system
evidence. Sleeps inside its virtual-time bubble differ from wall-clock delays.
- Keep each operation's timeout and cleanup bounded. A slow polling callback can
outlive the assertion budget if its own I/O has no cancellation.
Flake Triage And Completion
Reproduce one failing test with its original environment, then vary a relevant
dimension: ordering, workers, parallelism, timing, or shared state. Use
-race for suspected races and -count=N or -shuffle=on for repetition
and order dependence; choose the package and run budget from the risk.
A finite passing sample does not prove that a flake is impossible.
Distinguish a data race, product defect, fixture bug, resource leak, unsupported
runtime, and external dependency failure. Fix causes without weakening the
oracle. Quarantine only under the repo's accepted policy with a reason, owner,
tracking reference, and revisit condition.
Stop when the changed claim has proportionate evidence and further similar
tests would add little confidence. Report material untested behavior and the
next useful check. A completed review may contain unresolved findings.
Return the claim, seam, cases, oracles, exact commands/results, and residual risk
when they help assess the work; collapse these into a short note for a small edit.
References
Load only the detail relevant to the task:
- Assertion patterns: equality, errors,
async assertions, and helpers.
- Mocking patterns: fakes, expectations,
argument matching, pointer mutation, and ordering.
- Real boundaries: HTTP, database,
filesystem, clocks, and worker lifecycle examples.
- Suites and parallelism: lifecycle,
subtests, Go-version constraints, and process-wide state.
- Pressure scenarios: maintainer or contentious
review cases; distinguish static assessment from observed behavior.
- Coverage and validation: maintainer
source map, routing boundaries, and evidence limitations.
1---2name: go-testing-with-testify3description: Write, review, or harden Go tests using stretchr/testify assert, require, mock, or suite. Use for assertion choice, test doubles, subtests, concurrency, and flake triage in an existing Go test setup. Use coding-guidance-go for production code or non-testify tests and tester-mindset for test strategy without concrete test code.4---56# Go Testing With Testify78Turn a concrete behavior claim into useful Go test evidence. Preserve the9repository's Go version, test framework, and supported execution environment.1011## Route And Select Activity1213- **Implement or harden:** edit the requested tests and any explicitly covered14 production seam. A test request does not automatically authorize an unrelated15 production refactor, new dependency, or external service.16- **Review:** inspect tests and report prioritized findings, evidence, and17 consequences. Do not edit files or require remediation to complete a review.18- **Diagnose:** reproduce the named failure in scope, distinguish a product19 defect from a test-apparatus defect, then fix it when the request includes repair.2021Use `coding-guidance-go` for production Go or non-testify tests. Preserve22Ginkgo, Gomega, go-cmp-only, and other intentional stacks. Module bootstrap or23test-tool installation is separate from test authoring.2425Add `tester-mindset` when claims, oracles, or test strategy are still unclear.26Add `backend-guidance` or `backend-systems-guidance` only when a service,27repository, queue, or other backend boundary needs that design guidance.28Use `security` first for a security-led review and add29`security-identity-access` when its identity scope applies. Routine tests of an30already-defined permission rule can remain here.3132## Workflow33341. Read the scoped tests, implementation, `go.mod`, fixtures, and relevant35 repository instructions. Identify the Go and testify versions, local test36 command, helpers, and available dependencies.372. Name the behavior claim, oracle, and smallest seam that can reveal a defect.38 Pure logic needs a unit check; protocol, query, serialization, or lifecycle39 behavior may need a real boundary. Passing checks support only their scope.403. Preserve the existing test shape when it is clear. Use table-driven subtests41 when cases share setup and assertions; use separate tests when they do not.42 Introduce helpers, fakes, mocks, or suites only for a concrete benefit.434. For implementation, exercise the real code and assert the behavior being44 claimed. For review, assess these choices without rewriting the tests.455. Validate the narrowest changed test or package, for example46 `go test ./path/to/pkg -run '^TestName$' -count=1`. Add race checks,47 repetition, shuffle, or integration runs when the failure mode needs them.486. Inspect failures before changing assertions, retry counts, or timeouts.49 Report the exact evidence and remaining uncertainty.5051## Assertions And Oracles5253- Put expected before actual in testify comparisons.54- Use `require` for prerequisites whose failure makes later checks unsafe or55 meaningless; use `assert` for independent checks. A fatal failure in a56 subtest stops that subtest, not its siblings.57- Call `require.*`, `t.Fatal`, and `t.FailNow` only on the test goroutine.58 Return observations or errors from HTTP handlers and workers through a59 synchronized channel or result, then assert on the test goroutine.60- Use `ErrorIs` or `ErrorAs` when error identity or type is the contract.61 Error presence alone is sufficient when that is all the contract promises.62 Assert text only when it is intended to be stable.63- Use equality, unordered comparison, identity, structural JSON, or tolerances64 according to the contract. Exact float equality is valid for exact expected65 values; approximate computations need justified tolerances.66- Check fields the behavior promises, including generated IDs, defaults, or67 timestamps when relevant. Whole-struct equality is valid when the full value68 is the contract; do not discard meaningful fields merely to avoid failures.69- A success-only test can be meaningful. Add separate failure cases where they70 protect required behavior; do not require every individual test to exercise71 both success and failure.72- Call `t.Helper()` in helpers whose failures should identify their caller.7374Reject tautologies, mocks that replace the behavior being proved, and assertions75that cannot detect a plausible defect in the stated claim. A no-error assertion76can prove a narrow validation contract but does not prove that a user was stored77or a file's contents are correct. Logs can be the oracle when logging itself is78the requested behavior.7980## Boundary And Double Choice8182Prefer an existing cheap real harness when the risk is at that boundary:8384- HTTP protocol behavior: `httptest.NewServer`;85- filesystem behavior: `t.TempDir()`;86- database queries or transactions: a disposable dialect-appropriate harness;87- time or async behavior: explicit synchronization, injected time, or supported88 virtual-time testing.8990Use a fake for a simple owned collaborator; use `testify/mock` when argument,91call-count, failure, or ordering expectations improve the test. Respect existing92test seams. Do not introduce wrappers or containers solely to satisfy a generic93mock rule, and do not contact live or metered services without authority.9495Wire the double into the subject under test. Assert mock expectations after96owned asynchronous calls finish, using cleanup when it must also run after a97fatal test failure. Keep matchers specific to the promised interaction and98avoid retaining mutable pointer arguments as if they were immutable snapshots.99100## Concurrency, Time, And Cleanup101102- Add `t.Parallel()` only after checking shared state, database schemas, ports,103 temporary paths, environment, current directory, and goroutine lifetimes.104 `t.Setenv` and `t.Chdir` cannot be used in parallel tests or with parallel105 ancestors; check helper availability against the supported Go version.106- Testify `suite` does not support parallel tests. Keep suites when their107 scenario and lifecycle organization helps; do not migrate them for style alone.108- Go 1.22+ loop semantics depend on the module language version. Rebind loop109 variables where older semantics require it, not as mandatory modern boilerplate.110- Register cleanup next to acquisition. Own cancellation and wait for workers111 before test teardown; a deadline limits waiting but does not itself join work.112 `t.Context()` is available from Go 1.24 and cancels before cleanup.113- Prefer completion signals to polling. Use bounded `Eventually` only for a114 genuinely eventual observable state and synchronize shared reads.115- On Go 1.25+, consider `testing/synctest` for compatible in-process concurrent116 code. Virtual time is not a replacement for real network or external-system117 evidence. Sleeps inside its virtual-time bubble differ from wall-clock delays.118- Keep each operation's timeout and cleanup bounded. A slow polling callback can119 outlive the assertion budget if its own I/O has no cancellation.120121## Flake Triage And Completion122123Reproduce one failing test with its original environment, then vary a relevant124dimension: ordering, workers, parallelism, timing, or shared state. Use125`-race` for suspected races and `-count=N` or `-shuffle=on` for repetition126and order dependence; choose the package and run budget from the risk.127A finite passing sample does not prove that a flake is impossible.128129Distinguish a data race, product defect, fixture bug, resource leak, unsupported130runtime, and external dependency failure. Fix causes without weakening the131oracle. Quarantine only under the repo's accepted policy with a reason, owner,132tracking reference, and revisit condition.133134Stop when the changed claim has proportionate evidence and further similar135tests would add little confidence. Report material untested behavior and the136next useful check. A completed review may contain unresolved findings.137138Return the claim, seam, cases, oracles, exact commands/results, and residual risk139when they help assess the work; collapse these into a short note for a small edit.140141## References142143Load only the detail relevant to the task:144145- [Assertion patterns](references/assertion-patterns.md): equality, errors,146 async assertions, and helpers.147- [Mocking patterns](references/mocking-patterns.md): fakes, expectations,148 argument matching, pointer mutation, and ordering.149- [Real boundaries](references/real-boundary-patterns.md): HTTP, database,150 filesystem, clocks, and worker lifecycle examples.151- [Suites and parallelism](references/suite-and-parallelism.md): lifecycle,152 subtests, Go-version constraints, and process-wide state.153- [Pressure scenarios](references/pressure-tests.md): maintainer or contentious154 review cases; distinguish static assessment from observed behavior.155- [Coverage and validation](references/coverage-and-validation.md): maintainer156 source map, routing boundaries, and evidence limitations.