Loading...
Loading...
Go testing patterns and methodology: table-driven tests, t.Run subtests, t.Helper helpers, mocking interfaces, benchmarks, race detection, and synctest. Use when writing new Go tests, modifying existing tests, adding coverage, fixing failing tests, writing benchmarks, or creating mocks. Triggered by "go test", "_test.go", "table-driven", "t.Run", "benchmark", "mock", "race detection", "test coverage". Do NOT use for non-Go testing (use test-driven-development instead), debugging test failures (use systematic-debugging), or general Go development without test focus (use golang-general-engineer directly).
npx skill4agent add notque/claude-code-toolkit go-testingt.Runt.Helper()go testgo test -racepackage_testpackageTestConsumeLoop_CanBeReenteredAfterFailureTestRestartLoop_RestartsAfterTransientFailureconsumeLoopStart()t.Parallel()t.Cleanup()t.Context()b.Loop()for i := 0; i < b.N; i++go_diagnostics-coverprofiletesting/synctestbenchstatscripts/gen-table-test.shbash scripts/gen-table-test.sh --helpscripts/bench-compare.shbash scripts/bench-compare.sh --helpt.Runt.Helper()b.Loop()systematic-debuggingtest-driven-developmentgolang-general-engineer| Need | Test Type | Pattern |
|---|---|---|
| Multiple input/output cases | Table-driven unit test | |
| Single specific behavior | Focused unit test | Standard |
| Cross-component interaction | Integration test | Setup/teardown helpers |
| Performance measurement | Benchmark | |
| API usage documentation | Example test | |
package mypackage_test // Black-box testing (preferred)
import (
"testing"
"mymodule/mypackage"
)
// Order: Unit tests, Integration tests, Benchmarks, Examplesfunc TestParseConfig(t *testing.T) {
tests := []struct {
name string
input string
want Config
wantErr bool
}{
{
name: "valid YAML",
input: `key: value`,
want: Config{Key: "value"},
},
{
name: "invalid syntax",
input: `{{{`,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseConfig(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("ParseConfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && got != tt.want {
t.Errorf("ParseConfig() = %v, want %v", got, tt.want)
}
})
}
}func assertEqual[T comparable](t *testing.T, got, want T) {
t.Helper() // MUST be first line
if got != want {
t.Errorf("got %v, want %v", got, want)
}
}type MockStore struct {
GetFunc func(key string) ([]byte, error)
}
func (m *MockStore) Get(key string) ([]byte, error) {
if m.GetFunc != nil {
return m.GetFunc(key)
}
return nil, nil
}references/go-test-patterns.mdfor _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
// test body (use tt directly in Go 1.22+)
})
}t.Helper()# Standard run with verbose output
go test -v ./path/to/package/...
# With race detector (REQUIRED for concurrent code)
go test -race -v ./path/to/package/...
# With coverage
go test -coverprofile=coverage.out ./path/to/package/...
go tool cover -func=coverage.outgo test ./...t.Helper()t.Cleanupdeferb.Loop()func BenchmarkProcess(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
_ = Process(input)
}
}func BenchmarkBuilder(b *testing.B) {
b.Run("strings.Builder", func(b *testing.B) {
b.ReportAllocs()
for b.Loop() { /* ... */ }
})
b.Run("bytes.Buffer", func(b *testing.B) {
b.ReportAllocs()
for b.Loop() { /* ... */ }
})
}go test -bench=. -benchmem ./...references/go-benchmark-and-concurrency.mdgo test ./... # Run all tests
go test -v ./... # Verbose output
go test -race ./... # Race detector
go test -run TestMyFunc ./... # Specific test
go test -run TestMyFunc/subtest ./... # Specific subtest
go test -coverprofile=coverage.out ./... # Coverage profile
go tool cover -func=coverage.out # Coverage summary
go tool cover -html=coverage.out # Coverage HTML
go test -bench=. -benchmem ./... # Benchmarks
go test -short ./... # Skip long tests
go test -timeout 30s ./... # With timeout
go test -count=10 ./... # Detect flaky testssync.Mutexatomicreferences/go-benchmark-and-concurrency.mdt.TempDir()t.Setenv()time.Sleepsynctest.TestTestParseValidTestParseInvalidTestParseEmptyTestParset.Runt.Helper()t.Cleanup()os.ReadFile("testdata/input.json")t.TempDir()os.Getwd()| Rationalization | Why It's Wrong | Required Action |
|---|---|---|
| "One test case, no need for table-driven" | Will grow to multiple cases | Set up table-driven from the start |
| "t.Helper() is just cosmetic" | Wrong error location wastes debug time | Always add t.Helper() |
| "Tests pass, no need for -race" | Race conditions are silent until production | Run with -race for concurrent code |
| "Coverage is 80%, good enough" | What's in the uncovered 20%? | Check that critical paths are covered |
| "Mock is too complex to build" | Complex ≠ optional | Build the mock, track calls |
${CLAUDE_SKILL_DIR}/references/go-test-patterns.md${CLAUDE_SKILL_DIR}/references/go-benchmark-and-concurrency.md