cxuu/go-testing
Use when writing, reviewing, or improving Go test code — including table-driven tests, subtests, parallel tests, test helpers, test doubles, and assertions with cmp.Diff. Also use when a user asks to write a test for a Go function, even if they don't mention specific patterns like table-driven tests or subtests. Does not cover benchmark performance testing (see go-performance).
npx skills add https://github.com/cxuu/golang-skills --skill go-testing
> Compatibility: Diff examples may use github.com/google/go-cmp.
scripts/gen-table-test.sh - Run when generating a table-driven test scaffold.assets/table-test-template.go - Use as a copyable table-test starting point.references/TABLE-DRIVEN-TESTS.md - Read when choosing table tests, subtests, or parallel test patterns.references/TEST-HELPERS.md - Read when writing helpers, fixtures, cleanup, or test doubles.references/TEST-ORGANIZATION.md - Read when structuring packages, black-box tests, or larger test suites.references/VALIDATION-APIS.md - Read when choosing t.Error, t.Fatal, cmp.Diff, or assertion style.references/INTEGRATION.md - Read when testing external services, HTTP handlers, databases, or long-running setup.| Pattern | Use When |
|---------|----------|
| t.Error | Default — report failure, keep running |
| t.Fatal | Setup failed or continuing is meaningless |
| cmp.Diff | Comparing structs, slices, maps, protos |
| Table-driven | Many cases share identical logic |
| Subtests | Need filtering, parallel execution, or naming |
| t.Helper() | Any test helper function (call as first statement) |
| t.Cleanup() | Teardown in helpers instead of defer |
> Normative: Test failures must be diagnosable without reading the test
> source.
Every failure message must include: function name, inputs, actual (got), and
expected (want). Use the format YourFunc(%v) = %v, want %v.
// Good:
t.Errorf("Add(2, 3) = %d, want %d", got, 5)
// Bad: Missing function name and inputs
t.Errorf("got %d, want %d", got, 5)
Always print got before want: got %v, want %v — never reversed.
> Normative: Do not use assertion libraries. Use cmp.Diff for complex
> comparisons.
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("GetPost() mismatch (-want +got):\n%s", diff)
}
For protocol buffers, add protocmp.Transform() as a cmp option. Always
include the direction key (-want +got) in diff messages. Avoid comparing
JSON/serialized output — compare semantically instead.
> Normative: Use t.Error by default to report all failures in one run.
> Use t.Fatal only when continuing is impossible.
Choose t.Fatal when:
encode)
Never call t.Fatal/t.FailNow from a goroutine other than the test
goroutine — use t.Error instead.
> See assets/table-test-template.go when scaffolding a new table-driven test and need the canonical struct, loop, and subtest layout.
> Advisory: Use table-driven tests when many cases share identical logic.
Use table tests when: all cases run the same code path with no conditional
setup, mocking, or assertions. A single shouldErr bool is acceptable.
Don't use table tests when: cases need complex setup, conditional mocking,
or multiple branches — write separate test functions instead.
Key rules:
> Validation: After generating or modifying tests, run go test -run TestXxx -v to verify the tests compile and pass. Fix any compilation errors before proceeding.
> Normative: Test helpers must call t.Helper() first and use t.Cleanup()
> for teardown.
func setupTestDB(t *testing.T) *sql.DB {
t.Helper()
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatalf("Could not open database: %v", err)
}
t.Cleanup(func() { db.Close() })
return db
}
> Advisory: Test error semantics, not error message strings.
// Bad: Brittle string comparison
if err.Error() != "invalid input" { ... }
// Good: Semantic check
if !errors.Is(err, ErrInvalidInput) { ... }
For simple presence checks when specific semantics don't matter:
if gotErr := err != nil; gotErr != tt.wantErr {
t.Errorf("f(%v) error = %v, want error presence = %t", tt.input, err, tt.wantErr)
}
errors.Is/errors.As or sentinel errorsTake cxuu/go-testing from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.