t.Run biến mỗi case thành một subtest độc lập: có tên riêng trong output, chạy lẻ được bằng go test -run TestX/case_name, và một case fail không dừng các case còn lại.
go
func TestSlugify(t *testing.T) {
tests := []struct {
name, in, want string
}{
{"space", "hello world", "hello-world"},
{"upper", "Go Lang", "go-lang"},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := Slugify(tc.in); got != tc.want {
t.Errorf("Slugify(%q) = %q, want %q", tc.in, got, tc.want)
}
})
}
}Bẫy cần biết:
- Trước Go 1.22, biến loop tc dùng chung một ô nhớ; kết hợp t.Parallel thì subtest chỉ thấy phần tử cuối cùng — nên phải viết tc := tc. Từ Go 1.22 mỗi vòng lặp có biến mới nên bẫy này biến mất, nhưng vẫn hay bị hỏi vì code cũ đầy dòng tc := tc.
- Subtest gọi t.Parallel sẽ tạm dừng và chỉ chạy sau khi hàm test cha trả về. Vì vậy defer cleanup() đặt ở hàm cha sẽ chạy trước khi subtest xong — dùng t.Cleanup thay thế.
- Đừng dùng t.Fatal bên trong goroutine phụ; chỉ hợp lệ trên goroutine của chính test đó.