Funcții care returnează erori
Multe funcții Go returnează două valori: rezultatul și o eroare de tip error. La succes, eroarea este nil; la eșec, rezultatul este o valoare zero, iar eroarea explică ce a mers prost.
Un test pentru o astfel de funcție trebuie să verifice ambele căi: intrarea bună returnează valoarea corectă și o eroare nil, iar intrarea greșită chiar returnează o eroare — o funcție care acceptă în tăcere o intrare greșită este și ea un bug.
Calea bună
Pentru o intrare validă, verifică mai întâi eroarea: dacă nu este nil, ceva este profund greșit și nu are sens să verifici valoarea — asta este treaba lui t.Fatalf. Apoi compară valoarea.
Intrare validă, eroare nil
SafeDivide(10, 2) trebuie să returneze 5 și nicio eroare. Eroarea este verificată prima, cu t.Fatalf; abia apoi este comparată valoarea.
Ieșire: program Go și rezultatul capturat
package calc
import (
"errors"
"testing"
)
func SafeDivide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("nu se poate împărți la zero")
}
return a / b, nil
}
func TestSafeDivideGood(t *testing.T) {
got, err := SafeDivide(10, 2)
if err != nil {
t.Fatalf("eroare neașteptată: %v", err)
}
if got != 5 {
t.Errorf("SafeDivide(10, 2) = %v; așteptat 5", got)
}
}
Calea greșită
Cealaltă jumătate a contractului: o intrare greșită trebuie să producă o eroare. Testul apelează funcția cu intrarea greșită și eșuează dacă eroarea este nil.
Valoarea rezultat este ignorată cu _ — când se întoarce o eroare, valoarea nu mai are niciun înțeles.
Intrarea greșită trebuie să returneze o eroare
Împărțirea la zero trebuie să eșueze. Dacă err vine înapoi nil, testul eșuează cu t.Fatal — funcția a acceptat o intrare pe care ar fi trebuit să o respingă.
Ieșire: program Go și rezultatul capturat
package calc
import (
"errors"
"testing"
)
func SafeDivide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("nu se poate împărți la zero")
}
return a / b, nil
}
func TestSafeDivideByZero(t *testing.T) {
_, err := SafeDivide(10, 0)
if err == nil {
t.Fatal("se aștepta o eroare la împărțirea la zero, obținut nil")
}
}
Un tabel cu wantErr
Ambele căi încap într-un singur test bazat pe tabel: dă-i fiecărui caz de test un câmp wantErr, care spune dacă se așteaptă o eroare.
Bucla verifică mai întâi partea de eroare — o eroare primită când nu era așteptată niciuna, sau nicio eroare când era așteptată una, este fatală pentru acel subtest. Pentru cazurile bune, compară apoi valoarea, ca de obicei.
Cazuri bune și greșite într-un singur tabel
Patru cazuri: trei împărțiri normale și o împărțire la zero, cu wantErr: true. O singură buclă acoperă tot contractul funcției.
Ieșire: program Go și rezultatul capturat
package calc
import (
"errors"
"testing"
)
func SafeDivide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("nu se poate împărți la zero")
}
return a / b, nil
}
func TestSafeDivide(t *testing.T) {
cases := []struct {
name string
a, b float64
want float64
wantErr bool
}{
{"simple division", 10, 2, 5, false},
{"negative result", -9, 3, -3, false},
{"divide zero", 0, 5, 0, false},
{"divide by zero", 10, 0, 0, true},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got, err := SafeDivide(tc.a, tc.b)
if (err != nil) != tc.wantErr {
t.Fatalf("SafeDivide(%v, %v) eroare = %v; eroare așteptată %v", tc.a, tc.b, err, tc.wantErr)
}
if !tc.wantErr && got != tc.want {
t.Errorf("SafeDivide(%v, %v) = %v; așteptat %v", tc.a, tc.b, got, tc.want)
}
})
}
}