Un singur test, multe cazuri
Majoritatea funcțiilor au nevoie de aceeași verificare pentru multe intrări: valori normale, valori negative, zero, cazuri-limită. Dacă copiezi testul de cinci ori, ai cinci locuri de întreținut.
Modul Go este testul bazat pe tabel: pui cazurile de test într-un slice de structuri — fiecare caz conține un nume, intrările și rezultatul așteptat — și le parcurgi cu o singură buclă.
Adăugarea unui caz nou devine o singură linie nouă în tabel. Acesta este cel mai des întâlnit tipar de test din codul Go real.
Tabelul
Tabelul este un slice de structuri anonime, definit chiar în interiorul testului. Fiecare element este un caz de test: un nume (pentru mesajul de eșec), intrările și rezultatul așteptat.
Bucla apelează funcția o dată pentru fiecare caz și raportează fiecare nepotrivire cu t.Errorf — astfel apar toate cazurile greșite, nu doar primul.
Patru cazuri într-un singur tabel
Patru cazuri, o buclă, o verificare. Toate trec aici. Ca să testezi o a cincea situație, ai adăuga o linie în tabel — bucla rămâne neatinsă.
Ieșire: program Go și rezultatul capturat
package calc
import "testing"
func Add(a, b int) int {
return a + b
}
func TestAdd(t *testing.T) {
cases := []struct {
name string
a, b int
want int
}{
{"one plus one", 1, 1, 2},
{"positive and negative", 5, -3, 2},
{"both negative", -2, -4, -6},
{"zeros", 0, 0, 0},
}
for _, tc := range cases {
got := Add(tc.a, tc.b)
if got != tc.want {
t.Errorf("%s: Add(%d, %d) = %d; așteptat %d", tc.name, tc.a, tc.b, got, tc.want)
}
}
}
Subteste cu t.Run
t.Run(name, func(t *testing.T) { … }) transformă fiecare caz din tabel într-un subtest separat. Numele cazurilor apar acum în ieșirea go test -v, de exemplu TestAdd/both_negative (spațiile devin liniuțe de subliniere).
Fiecare subtest trece sau eșuează pe cont propriu, ceea ce face ca un caz stricat să fie ușor de observat într-un tabel lung.
Fiecare caz primește un nume
Același tabel, împachetat în t.Run. Ieșirea detaliată listează acum toate cele patru cazuri separat — vezi exact care cazuri au rulat și au trecut.
Ieșire: program Go și rezultatul capturat
package calc
import "testing"
func Add(a, b int) int {
return a + b
}
func TestAdd(t *testing.T) {
cases := []struct {
name string
a, b int
want int
}{
{"one plus one", 1, 1, 2},
{"positive and negative", 5, -3, 2},
{"both negative", -2, -4, -6},
{"zeros", 0, 0, 0},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := Add(tc.a, tc.b)
if got != tc.want {
t.Errorf("Add(%d, %d) = %d; așteptat %d", tc.a, tc.b, got, tc.want)
}
})
}
}
Rularea unui singur caz
Subtestele pot fi selectate din linia de comandă cu -run, folosind TestName/subtest_name:
go test -v -run "TestAdd/both_negative"
Rulează doar subtestul care se potrivește. Este perfect când repari un caz stricat: rulezi din nou doar acel caz până când trece, apoi rulezi din nou totul.
Filtrare cu -run
Același fișier ca mai sus, rulat cu -run "TestAdd/both_negative". Se execută doar unul dintre cele patru cazuri — celelalte nici măcar nu sunt pornite.
Ieșire: program Go și rezultatul capturat
package calc
import "testing"
func Add(a, b int) int {
return a + b
}
func TestAdd(t *testing.T) {
cases := []struct {
name string
a, b int
want int
}{
{"one plus one", 1, 1, 2},
{"positive and negative", 5, -3, 2},
{"both negative", -2, -4, -6},
{"zeros", 0, 0, 0},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := Add(tc.a, tc.b)
if got != tc.want {
t.Errorf("Add(%d, %d) = %d; așteptat %d", tc.a, tc.b, got, tc.want)
}
})
}
}