מודול 9 · שיעור 2 מתוך 542/50 בקורס~14 min
שיעורי מודול (2/5)
מבחנים מונחי שולחן
מבחנים מונחי טבלה הם הדפוס האידיומטי של Go: במקום לכתוב N פונקציות TestThisSpecificCase נפרדות, אתה מצהיר על חלק של מקרים וחוזר על עצמו. יש בלוק קביעה אחד, ונתוני הבדיקה מופרדים מהלוגיקה.
תבנית הבסיס
Go
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)
}
})
}
}שלושה מרכיבים:
- חתך של מבנה אנונימי עם השדות הרגילים:
name, התשומות, הפלט הצפוי (ואולי שגיאה צפויה). for _, tc := range cases- לולאת הביצוע.t.Run(tc.name, func(t *testing.T) { ... })— כל מקרה הופך לתת-מבחן עם השם שלו.
למה t.Run?
t.Run יוצר מבחן משנה הכולל:
- שם הפרט מוצג בפלט:
TestAbs/pos,TestAbs/neg, ... - שלו
t *testing.T: כשל במקרה אחד לא עוצר את האחרים (אלא אם כן נעשה שימוש ב-t.Fatal). - סינון שורת פקודה:
go test -run TestAbs/negפועל רק במקרה זה. - מקביליות אופציונלית: התקשר ל-
t.Parallel()בתוך ה-func כדי לבצע מקבילה.
Bash
go test -v -run TestAbs/negמקרים עם שגיאות צפויות
Go
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)
}
})
}מלכודת סגירת הלולאה (pre-Go 1.22)
בגרסאות שלפני Go 1.22, המשתנה tc שותף על פני איטרציות: קריאה ל-t.Parallel() בתוך t.Run גרמה לכל בדיקות המשנה לפעול נגד אותו מקרה אחרון. התיקון היה:
Go
for _, tc := range cases {
tc := tc // local rebind (pre-1.22)
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
// ...
})
}מתן שמות למקרים
שיטות עבודה מומלצות:
- שם קצר אך מעורר:
"empty input","too large","negative". - אין רווחים אם אתה רוצה לסנן בקלות עם
-run(או להשתמש בקו תחתון). - כלול תמיד מקרה של "שביל שמח", מקרה של "קצה" (אפס, ריק) ולפחות מקרה שגיאה אחד.
תרגילים
פעילות גופנית#go.m9.l2.e1
ניסיונות: 0טוען...
הגדר נתח מקרים של מבנה אנונימי עם שדות ב-ורצוי, מאוכלס ב-3 מקרים לבדיקת Abs (חיובי, שלילי, אפס).
טוען עורך...
הפתרון זמין לאחר 3 ניסיונות
פעילות גופנית#go.m9.l2.e2
ניסיונות: 0טוען...
הוסף את t.Run עם tc.name כדי להפוך כל מקרה לתת-מבחן בעל שם.
טוען עורך...
הפתרון זמין לאחר 3 ניסיונות
Quiz#go.m9.l2.e3
מוכןמה היתרון העיקרי של התבנית מונעת הטבלה על פני N פונקציות מבחן נפרדות?
Go
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) { ... })
}