golang-testing
SKILL.md
Go Testing Patterns
Table-Driven Tests
The standard Go testing pattern:
func TestParseAmount(t *testing.T) {
tests := []struct {
name string
input string
want float64
wantErr bool
}{
{"valid integer", "100", 100.0, false},
{"valid decimal", "99.99", 99.99, false},
{"with currency", "$50.00", 50.0, false},
{"negative", "-10", -10.0, false},
{"empty string", "", 0, true},
{"not a number", "abc", 0, true},
{"overflow", "9999999999999999999", 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseAmount(tt.input)
if (err != nil) != tt.wantErr {
t.Fatalf("ParseAmount(%q) error = %v, wantErr %v",
tt.input, err, tt.wantErr)
}
if !tt.wantErr && got != tt.want {
t.Errorf("ParseAmount(%q) = %v, want %v",
tt.input, got, tt.want)
}
})
}
}
HTTP Handler Testing
func TestGetUserHandler(t *testing.T) {
repo := &mockUserRepo{
users: map[string]*User{
"1": {ID: "1", Name: "Alice"},
},
}
handler := NewUserHandler(repo)
req := httptest.NewRequest("GET", "/users/1", nil)
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
require.Equal(t, http.StatusOK, rec.Code)
var user User
err := json.NewDecoder(rec.Body).Decode(&user)
require.NoError(t, err)
assert.Equal(t, "Alice", user.Name)
}
Subtests for Setup/Teardown
func TestUserService(t *testing.T) {
db := setupTestDB(t)
svc := NewUserService(db)
t.Run("Create", func(t *testing.T) {
user, err := svc.Create(context.Background(), "Alice", "alice@example.com")
require.NoError(t, err)
assert.Equal(t, "Alice", user.Name)
})
t.Run("FindByEmail", func(t *testing.T) {
user, err := svc.FindByEmail(context.Background(), "alice@example.com")
require.NoError(t, err)
assert.Equal(t, "Alice", user.Name)
})
t.Run("NotFound", func(t *testing.T) {
_, err := svc.FindByEmail(context.Background(), "missing@example.com")
assert.ErrorIs(t, err, ErrNotFound)
})
}
Benchmarks
func BenchmarkParseAmount(b *testing.B) {
inputs := []string{"100", "99.99", "$50.00", "-10.5"}
for _, input := range inputs {
b.Run(input, func(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseAmount(input)
}
})
}
}
// Run: go test -bench=. -benchmem ./...
Fuzzing (Go 1.18+)
func FuzzParseAmount(f *testing.F) {
// Seed corpus
f.Add("100")
f.Add("99.99")
f.Add("$50.00")
f.Add("")
f.Fuzz(func(t *testing.T, input string) {
result, err := ParseAmount(input)
if err == nil && result < 0 {
// Verify invariants on successful parse
t.Errorf("ParseAmount(%q) = %v, expected non-negative", input, result)
}
})
}
// Run: go test -fuzz=FuzzParseAmount ./...
Race Detection
# Always run with -race in CI
go test -race -count=1 ./...
Test Helpers
// testutil/helpers.go
func RequireEqualJSON(t *testing.T, expected, actual string) {
t.Helper()
var e, a interface{}
require.NoError(t, json.Unmarshal([]byte(expected), &e))
require.NoError(t, json.Unmarshal([]byte(actual), &a))
assert.Equal(t, e, a)
}
Commands
go test ./... # Run all tests
go test -v ./... # Verbose output
go test -race ./... # Race detection
go test -cover ./... # Coverage summary
go test -coverprofile=c.out ./... # Coverage profile
go tool cover -html=c.out # Visual coverage report
go test -bench=. -benchmem ./... # Benchmarks
go test -fuzz=FuzzName ./pkg # Fuzzing
go test -count=1 ./... # Disable test caching
Weekly Installs
2
Repository
peopleforrester…dotfilesGitHub Stars
1
First Seen
14 days ago
Security Audits
Installed on
opencode2
gemini-cli2
codebuddy2
github-copilot2
codex2
kimi-cli2