Bài học theo mô-đun (2/5)
Kiểm tra dựa trên bảng
Kiểm tra dựa trên bảng là mẫu đặc trưng của Go: thay vì viết N hàm TestThisSpecificCase riêng biệt, bạn khai báo một lát trường hợp và lặp lại. Có MỘT khối xác nhận và dữ liệu thử nghiệm được tách khỏi logic.
Mẫu cơ sở
func TestAbs(t *testing.T) {
cases := []struct {
name string
in int
want int
}{
{"pos", 3, 3},
{"neg", -3, 3},
{"zero", 0, 0},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := Abs(tc.in); got != tc.want {
t.Errorf("Abs(%d) = %d; voglio %d", tc.in, got, tc.want)
}
})
}
}Ba thành phần:
- Lát cấu trúc ẩn danh với các trường thông thường:
name, đầu vào, đầu ra dự kiến (và có thể là lỗi dự kiến). for _, tc := range cases— vòng lặp thực thi.t.Run(tc.name, func(t *testing.T) { ... })— mọi trường hợp đều trở thành một bài kiểm tra phụ có tên riêng.
Tại sao t.Run?
t.Run tạo một kiểm tra phụ có:
- Tên cá nhân hiển thị ở đầu ra:
TestAbs/pos,TestAbs/neg, ... t *testing.Tcủa chính nó: lỗi trong một trường hợp không ngăn được các trường hợp khác (trừ khi sử dụngt.Fatal).- Lọc dòng lệnh:
go test -run TestAbs/negchỉ chạy trường hợp đó. - Song song tùy chọn: gọi
t.Parallel()bên trong hàm để song song hóa.
go test -v -run TestAbs/negCác trường hợp có lỗi dự kiến
cases := []struct {
name string
in string
want int
wantErr bool
}{
{"ok", "42", 42, false},
{"bad", "x", 0, true},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got, err := Atoi(tc.in)
if (err != nil) != tc.wantErr {
t.Fatalf("err = %v, wantErr = %v", err, tc.wantErr)
}
if !tc.wantErr && got != tc.want {
t.Errorf("got %d, want %d", got, tc.want)
}
})
}Cạm bẫy đóng vòng lặp (trước phiên bản 1.22)
Trong các phiên bản trước Go 1.22, biến tc đã được chia sẻ qua nhiều lần lặp lại: việc gọi t.Parallel() bên trong t.Run đã khiến tất cả các thử nghiệm phụ chạy theo cùng một trường hợp cuối cùng. Cách khắc phục là:
for _, tc := range cases {
tc := tc // local rebind (pre-1.22)
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
// ...
})
}Đặt tên các trường hợp
Thực tiễn tốt nhất:
- Tên ngắn gọn nhưng giàu sức gợi:
"empty input","too large","negative". - Không có khoảng trắng nếu bạn muốn lọc dễ dàng bằng
-run(hoặc sử dụng dấu gạch dưới). - Luôn bao gồm trường hợp "đường dẫn hạnh phúc", trường hợp "cạnh" (không, trống) và ít nhất một trường hợp lỗi.
Bài tập
Xác định một lát trường hợp của cấu trúc ẩn danh với các trường trong và mong muốn, được điền 3 trường hợp để kiểm tra Abs (dương, âm, 0).
Giải pháp khả dụng sau 3 lần thử
Thêm t.Run với tc.name để biến mỗi trường hợp thành một bài kiểm tra phụ được đặt tên.
Giải pháp khả dụng sau 3 lần thử
Ưu điểm chính của mẫu định hướng bảng so với N hàm Kiểm tra riêng biệt là gì?
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) { ... })
}