मॉड्यूल पाठ (2/5)
टेबल-संचालित परीक्षण
टेबल-संचालित परीक्षण गो के मुहावरेदार पैटर्न हैं: एन अलग TestThisSpecificCase फ़ंक्शन लिखने के बजाय, आप मामलों का एक टुकड़ा घोषित करते हैं और पुनरावृत्त करते हैं। एक अभिकथन ब्लॉक है, और परीक्षण डेटा को तर्क से अलग किया गया है।
आधार पैटर्न
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()को कॉल करें।
go test -v -run TestAbs/negअपेक्षित त्रुटियों वाले मामले
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)
}
})
}लूप बंद होने का ख़तरा (प्री-गो 1.22)
गो 1.22 से पहले के संस्करणों में, tc वैरिएबल को सभी पुनरावृत्तियों में साझा किया गया था: t.Run के अंदर t.Parallel() को कॉल करने से सभी उप-परीक्षण उसी अंतिम मामले के विरुद्ध चलने लगे। समाधान यह था:
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(या अंडरस्कोर का उपयोग करें) के साथ आसानी से फ़िल्टर करना चाहते हैं तो कोई रिक्त स्थान नहीं है। - हमेशा एक "हैप्पी पाथ" केस, एक "एज" केस (शून्य, खाली) और कम से कम एक त्रुटि केस शामिल करें।
अभ्यास
एब्स (सकारात्मक, नकारात्मक, शून्य) का परीक्षण करने के लिए 3 मामलों से भरे हुए फ़ील्ड के साथ अज्ञात संरचना के एक केस स्लाइस को परिभाषित करें।
3 प्रयासों के बाद समाधान उपलब्ध है
प्रत्येक मामले को नामित उप-परीक्षण में बदलने के लिए t.Run को tc.name के साथ जोड़ें।
3 प्रयासों के बाद समाधान उपलब्ध है
एन अलग-अलग टेस्ट फ़ंक्शंस की तुलना में टेबल-संचालित पैटर्न का मुख्य लाभ क्या है?
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) { ... })
}