Write and refactor Go tests into table-driven form, including subtests, clear case names, deterministic fixtures, edge-case coverage, and parallel-safe patterns. Use for adding new unit tests, converting repetitive tests, or improving test readability and coverage in Go packages. Use proactively when multiple similar assertions appear or when new behavior needs many input/output cases. Examples: - user: "Add tests for this parser" -> create table-driven tests with named cases and edge inputs - user: "These tests are repetitive" -> refactor to a case table with t.Run subtests - user: "Cover error paths too" -> expand table with failure cases and explicit expected errors - user: "Make tests easier to read" -> normalize setup, expected values, and case naming
Use this workflow for Go test authoring in *_test.go files.
testCase struct with only needed fields.[]testCase literal with readable name values.t.Run(tc.name, func(t *testing.T) { ... }).func TestThing(t *testing.T) {
testCases := []struct {
name string
input string
want int
wantErr bool
}{
{name: "empty input", input: "", want: 0, wantErr: true},
{name: "single token", input: "a", want: 1, wantErr: false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := Thing(tc.input)
if (err != nil) != tc.wantErr {
t.Fatalf("Thing() error = %v, wantErr %v", err, tc.wantErr)
}
if got != tc.want {
t.Fatalf("Thing() = %d, want %d", got, tc.want)
}
})
}
}
testCases or cases consistently in a file.errors.Is.t.Helper() in shared assertion helpers.t.Parallel() only when test data and globals are isolated.go fmt ./...
go test ./...