golang-testing
Production-ready Golang tests — table-driven tests, testify suites and mocks, parallel tests, fuzzing, fixtures, goroutine leak detection with goleak, snapshot testing, code coverage, integration tests, idiomatic test naming. Use when writing or reviewing Go tests, choosing a testing approach, setti
By samber · 40,136 installs
npx skills add samber/cc-skills-golang --skill golang-testing
Source repository · Upstream listing
Persona: You are a Go engineer who treats tests as executable specifications. You write tests to constrain behavior, not to hit coverage targets.
Thinking mode: Reason as thoroughly as possible for test strategy design and failure analysis — shallow reasoning misses edge cases and produces brittle tests that pass today but break tomorrow. On Claude Code, use ultrathink to trigger extended thinking explicitly.
Orchestration mode: Fan out the three sub agents described in Audit mode (unit quality and coverage gaps, integration isolation, goroutine/race issues) for auditing a large test suite, and merge their findings into one gap report. On Claude Code, use ultracode to opt into multi agent orchestration explicitly.
Modes:
Write mode — generating new tests for existing or new code. Work sequentially through the code under test; use gotests to scaffold table driven tests, then enrich with edge cases and error paths.
Review mode — reviewing a PR's test changes. Focus on the diff: check coverage of new behaviour, assertion quality, table driven structure, and absence of flakiness patterns. Sequential.
Audit mode — auditing an existing test suite for gaps, flakiness, or bad patterns (order dependent tests, missing t.Parallel() , implementation detail coupling). Launch up to 3 parallel sub agents split by concern: (1) unit test quality and coverage gaps, (2) integration test isolation and build tags, (3) goroutine leaks and race conditions.
Debug mode — a test is failing or flaky. Work sequentially: reproduce reliably, isolate the failing assertion, trace the root cause in production code or test setup.
Community default. A company skill that explicitly supersedes samber/cc skills golang@golang testing skill takes precedence.
Dependencies:
gotests: go install github.com/cweill/gotests/gotests@latest
Go Testing Best Practices
This skill guides the creation of production ready tests for Go applications. Follow these principles to write maintainable, fast, and reliable tests.
Best Practices Summary
1. Table driven tests MUST use named subtests every test case needs a name field passed to t.Run
2. Integration tests MUST use build tags ( //go:build integration ) to separate from unit tests
3. Tests MUST NOT depend on execution order each test MUST be independently runnable
4. Independent tests SHOULD use t.Parallel() when possible
5. Tests MUST assert observable behavior and public API contracts, not implementation details a test coupled to internals turns every refactor into a test rewrite while proving nothing about the contract
6. Packages with goroutines SHOULD use goleak.VerifyTestMain in TestMain to detect goroutine leaks
7. Use testify as helpers, not a replacement for standard library
8. Mock interfaces, not concrete types
9. Keep unit tests fast (< 1ms), use build tags for integration tests
10. Run tests with race detection in CI
11. Include examples as executable documentation
12. Test files MUST be named after the source file under test, not after the function or method being tested
13. Test functions SHOULD appear in the same order as the functions/methods they test in the source file
Test Structure and Organization
File Conventions
Name the test file after the source file it tests, not after the function or method under test. Go's convention is one test file per source file ( foo.go foo test.go ), because tools ( go test , coverage reports, IDE "jump to test" navigation, gotests ) and reviewers all resolve tests by source file, not by symbol. A source file usually declares several functions/methods; splitting its tests by symbol name scatters them across many files and breaks that file to file mapping.
Exception: very large source files MAY be split into multiple test.go files by concern (e.g. foo test.go + foo edgecases test.go ), but each split file's name MUST still be derived from the source file name, never from an individual function name. Prefer keeping a single test.go file per source file even when it grows large — splitting adds navigation overhead and is rarely worth it; reach for the exception only when a single file becomes genuinely unwieldy to browse or review.
Within a test file, order test functions to match the order their tested functions/methods appear in the source file. A reader (human or agent) scrolling foo.go alongside foo test.go can then find the matching test by position instead of searching; drift between the two orderings compounds every time either file grows.
Naming Conventions
Table Driven Tests
Table driven tests are the idiomatic Go way to test multiple scenarios. Always name each test case.
Common Pitfall: Assert Scope Leaking into Subtests
Never create a testify assert / require instance in the parent test function and reuse it inside t.Run closures. assert.New(t) captures the exact testing.T it was built with, so if that t belongs to the parent, every failure raised inside the subtest gets attributed to the parent test in go test output — the failing subtest itself still reports PASS , silently hiding which case broke. This happens whether or not the subtest calls t.Parallel() .
Verify with a deliberately broken case: if go test v run TestName shows FAIL: TestName but every PASS: TestName/subtest name line still says PASS, the assert scope is leaking.
Unit Tests
Unit tests should be fast (< 1ms), isolated (no external dependencies), and deterministic.
Testing HTTP Handlers
Use httptest for handler tests with table driven patterns. See [HTTP Testing](./references/http testing.md) for examples with request/response bodies, query parameters, headers, and status code assertions.
Goroutine Leak Detection with goleak
Use go.uber.org/goleak to detect leaking goroutines, especially for concurrent code:
To exclude specific goroutine stacks (for known leaks or library goroutines):
Or per test:
testing/synctest for Deterministic Goroutine Testing
testing/synctest (Go 1.25+) provides deterministic tests for goroutines, timers, deadlines, and context cancellation. Time advances only when all goroutines are blocked, making ordering predictable.
When to use synctest instead of real time:
Testing concurrent code with time based operations (time.Sleep, time.After, time.Ticker)
When race conditions need to be reproducible
When tests are flaky due to timing issues
Use synctest.Test in Go 1.25+ and later. Do not use the old Go 1.24 experimental synctest.Run API in Go 1.25+ code. If a module explicitly targets Go 1.24 and opts into GOEXPERIMENT=synctest , use the old API only as a compatibility fallback.
Key differences in synctest :
time.Sleep advances synthetic time instantly when the goroutine blocks
time.After fires when synthetic time reaches the duration
All goroutines run to blocking points before time advances
Test execution is deterministic and repeatable
Go 1.27+ adds synctest.Sleep(d) as a direct helper to advance the bubble's fake clock, equivalent to time.Sleep(d) followed by synctest.Wait() but without needing a real goroutine to block on
Go 1.27+ also adds httptest.NewTestServer() , an in memory fake network variant of httptest.NewServer that composes with synctest — no real socket, so server tests can run inside a synctest.Test bubble instead of needing httptest.NewServer plus real timers.
Test Timeouts
For tests that may hang, use a timeout helper that panics with caller location. See [Helpers](./references/helpers.md).
Benchmarks
Write benchmarks as sub benchmarks ( b.Run per variant) so each variant gets its own name in the output — that name is what comparison tooling diffs. For Go 1.24+, use b.Loop() rather than a b.N loop.
→ See [Benchmarks in a Test Suite](./references/benchmarks.md) for the code shape and size parameterized examples.
→ See samber/cc skills golang@golang benchmark skill for measurement methodology: benchstat , profiling from benchmarks, and CI regression detection.
Go 1.26+: test artifacts
When a test, benchmark, or fuzz target needs to persist files for inspection, use ArtifactDir() instead of ad hoc paths or repo local output.
Available on testing.T , testing.B , and testing.F in Go 1.26+.
Go 1.27+: stdversion runs automatically
go test now invokes the stdversion vet check by default, flagging any use of an API newer than the module's go directive. A CI failure from this check means either the go directive needs bumping or the code needs to stop using the newer API — it is not a check to silence.
Parallel Tests
Use t.Parallel() to run tests concurrently:
Fuzzing
Use fuzzing to find edge cases and bugs:
Examples as Documentation
ExampleXxx functions are executable documentation: go test compares their stdout to the // Output: comment, so a drifting example fails the build instead of misleading readers.
→ See [Examples as Documentation](./references/examples.md) for naming rules, Unordered output , and placement.
Code Coverage
Generate a profile with go test coverprofile=coverage.out ./... , then read the uncovered lines with go tool cover html=coverage.out . Coverage locates untested paths; it does not measure assertion quality, so treat a percentage as a gap finder rather than a target.
→ See [Code Coverage](./references/coverage.md) for coverage modes, coverpkg , and reporting pitfalls.
Integration Tests
Use build tags to separate integration tests from unit tests:
Run integration tests separately:
For Docker Compose fixtures, SQL schemas, and integration test suites, see [Integration Testing](./references/integration testing.md).
Mocking
Mock interfaces, not concrete types. Define interfaces where consumed, then create mock implementations.
For mock patterns, test fixtures, and time mocking, see [Mocking](./references/mocking.md).
Enforce with Linters
Many test best practices are enforced automatically by linters: thelper , paralleltest , testifylint . See the samber/cc skills golang@golang lint skill for configuration and usage.
Cross References
→ See samber/cc skills golang@golang stretchr testify skill for detailed testify API (assert, require, mock, suite)
→ See samber/cc skills golang@golang database skill (testing.md) for database integration test patterns
→ See samber/cc skills golang@golang concurrency skill for goroutine leak detection with goleak
→ See samber/cc skills golang@golang continuous integration skill for CI test configuration and GitHub Actions workflows
→ See samber/cc skills golang@golang lint skill for testifylint and paralleltest configuration
→ See samber/cc skills golang@golang continuous integration skill for automated AI driven code review in CI using these guidelines
Quick Reference