Tips & tricks
Small idioms with a big payoff.
Table-driven tests
One test function, many cases, readable failures with t.Run.
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"zero", 0, 0, 0},
{"positive", 2, 3, 5},
{"negative", -2, -3, -5},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
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)
}
})
}
}Error wrapping
Add context while keeping the original error inspectable.
var ErrNotFound = errors.New("not found")
func load(id int) error {
return fmt.Errorf("load user %d: %w", id, ErrNotFound) // wrap with context
}
if err := load(7); errors.Is(err, ErrNotFound) { // still matches after wrapping
// handle 404
}Timeouts with context
Every outbound call gets a deadline in two lines.
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
resp, err := http.DefaultClient.Do(req) // aborts automatically after 2sSaved in this browser only. Part of Clean code foundations (0/5).