Reguli mici, diferență mare
Testele bune respectă câteva obiceiuri:
1. Nume clare — TestGreetEmptyName spune ce s-a stricat fără să deschizi fișierul.
2. Un singur comportament per test — testele mici arată direct spre problemă.
3. Tabele pentru multe cazuri — o singură buclă în loc de teste copiate.
4. Mesaje cu got și want — o eșuare trebuie să se explice singură.
5. t.Error ca să vezi tot, t.Fatal când nu are rost să continui.
6. Rulează testele des — după fiecare modificare mică, nu o singură dată la final.
7. Testele stau lângă cod — același folder, sufixul _test.go.
Mesaje de eșuare care ajută
Un test care eșuează este citit într-un moment de confuzie — mesajul trebuie să poarte toată povestea: ce apel, ce a venit înapoi, ce se aștepta.
Convenția Go este Func(input) = got; want want. Cu %q șirurile sunt puse între ghilimele, așa că spațiile sau semnele de punctuație lipsă se văd imediat.
Un mesaj cu toată povestea
Greet întoarce salutul greșit. Mesajul de eșuare arată apelul, valoarea got și valoarea want — eroarea (cuvânt greșit, virgulă și semn de exclamare lipsă) se vede doar din ieșire, fără să deschizi codul.
Ieșire: program Go și rezultatul capturat
package greet
import "testing"
// Greet are o eroare: cuvânt greșit, virgulă și „!” lipsă.
func Greet(name string) string {
return "Bună " + name
}
func TestGreet(t *testing.T) {
got := Greet("Ana")
want := "Salut, Ana!"
if got != want {
t.Errorf("Greet(%q) = %q; așteptat %q", "Ana", got, want)
}
}
Un singur comportament per test
Un test care verifică cinci lucruri are cinci motive să eșueze — iar numele lui poate descrie doar unul dintre ele. Împărțirea pe comportamente păstrează fiecare test mic și numele lui onest.
Aici salutul normal și varianta de rezervă pentru numele gol sunt două comportamente, așa că primesc două teste. Când unul eșuează, numele lui îți spune deja ce comportament s-a stricat.
Două comportamente, două teste
TestGreetName verifică cazul normal, TestGreetEmpty varianta de rezervă. Ieșirea detaliată le listează separat — o eșuare ar arăta exact spre un singur comportament.
Ieșire: program Go și rezultatul capturat
package greet
import "testing"
func Greet(name string) string {
if name == "" {
return "Salut, străine!"
}
return "Salut, " + name + "!"
}
func TestGreetName(t *testing.T) {
got := Greet("Ana")
if got != "Salut, Ana!" {
t.Errorf("Greet(%q) = %q; așteptat %q", "Ana", got, "Salut, Ana!")
}
}
func TestGreetEmpty(t *testing.T) {
got := Greet("")
if got != "Salut, străine!" {
t.Errorf("Greet(%q) = %q; așteptat %q", "", got, "Salut, străine!")
}
}
Cât este testat: acoperirea
go test -cover măsoară ce parte din codul tău execută efectiv testele, ca procent din instrucțiuni.
Numărul este o busolă, nu un scop: o valoare mică arată căi netestate — aici testul parcurge doar ramura mild, așa că ramurile freezing și hot nu sunt verificate niciodată. O acoperire de 100% nu dovedește că acel cod este corect, dar liniile neacoperite sunt sigur neverificate.
Măsoară acoperirea
Describe are trei ramuri, testul parcurge una. Numărul acoperirii o dezvăluie — adăugarea de teste pentru ramura de frig și cea de căldură l-ar face să crească.
Ieșire: program Go și rezultatul capturat
package temp
import "testing"
func Describe(celsius int) string {
if celsius < 0 {
return "freezing"
}
if celsius > 30 {
return "hot"
}
return "mild"
}
func TestDescribeMild(t *testing.T) {
got := Describe(20)
if got != "mild" {
t.Errorf("Describe(20) = %q; așteptat %q", got, "mild")
}
}