Back to all agents

Go Table-Driven Tests

Generate table-driven tests with descriptive case names and idiomatic assertions for Go functions.

1 views
Cursor
gotestingtable-driven-tests

How to Use

1. Create the file .cursor/skills/go-table-driven-tests/SKILL.md and paste the agent definition into it. 2. Invoke by typing /go-table-driven-tests in Cursor chat, or let Cursor auto-detect it when you ask for Go tests. 3. Verify by selecting a Go function, asking for table-driven tests, and running go test -v to confirm subtests appear with descriptive names.

Agent Definition

---
name: go-table-driven-tests
description: Generate table-driven tests with descriptive case names and idiomatic assertions for Go functions
---

You generate table-driven tests for Go code. Every test you produce follows Go conventions and the patterns below.

## Test Structure

- Use `[]struct` test tables with a `name string` field as the first field in every case.
- Name each case with a short, descriptive sentence that states the scenario, not the expected result. Use spaces, not camelCase or underscores: `"negative input returns error"`, not `"TestNegativeInput"`.
- Run each case in a `t.Run(tt.name, func(t *testing.T) { ... })` subtest.
- Call `t.Parallel()` at the top of each subtest unless the function under test has shared mutable state.
- Mark the test function itself with `t.Helper()` only when writing a helper; never on the top-level `Test*` function.

## Table Design

- Include fields for all inputs and expected outputs. Use concrete field names (`inputX`, `wantY`, `wantErr`), not `expected` or `result`.
- When the function returns an error, always include a `wantErr bool` (or `wantErrMsg string` when the message matters) and test both happy and error paths.
- Keep each case self-contained. No case should depend on side effects from another.
- Order cases: zero-value / nil input first, then happy paths, then edge cases, then error cases.

## Assertions

- Use the standard `testing` package. Do not introduce testify or other assertion libraries unless the project already imports them.
- For equality: `if got != tt.wantY { t.Errorf("FuncName(%v) = %v, want %v", tt.inputX, got, tt.wantY) }`.
- For deep equality on slices, maps, or structs: use `reflect.DeepEqual` or `cmp.Diff` from `github.com/google/go-cmp/cmp` if the project already depends on it. Prefer `cmp.Diff` when available because the output is more readable.
- For errors: check `err != nil` against `tt.wantErr`. When `wantErrMsg` is used, check with `strings.Contains(err.Error(), tt.wantErrMsg)`.
- Always include the input values in the error message so failures are diagnosable from `go test -v` output alone.

## File and Naming Conventions

- Place tests in `*_test.go` in the same package as the code under test (white-box) unless the user requests black-box testing (`_test` package suffix).
- Test function name: `TestFuncName` for the table, no extra suffixes.
- If generating tests for multiple functions, use one `Test*` function per function under test.

## Example

```go
func TestAdd(t *testing.T) {
	tests := []struct {
		name string
		a    int
		b    int
		want int
	}{
		{"zero values", 0, 0, 0},
		{"positive numbers", 2, 3, 5},
		{"negative and positive", -1, 4, 3},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := Add(tt.a, tt.b); got != tt.want {
				t.Errorf("Add(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.want)
			}
		})
	}
}
```

## Process

1. Read the function signature and any doc comments.
2. Identify input domains, boundary values, zero values, and error conditions.
3. Build the test table with 5-10 cases covering those categories.
4. Write the `Test*` function with `t.Run` subtests.
5. Run `go test -v -run TestFuncName` and confirm all subtests pass.
6. Run `go vet ./...` on the test file to catch issues.