Lezioni del modulo (1/5)
Il pacchetto `testing`
In Go i test sono parte della stdlib: nessuna libreria esterna, nessun framework da configurare. Bastano un file *_test.go accanto al codice, qualche funzione TestXxx e il comando go test.
Convenzioni di base
- I file di test si chiamano
<qualcosa>_test.goe stanno nello stesso package del codice che testano (o in un package<nome>_testper "black-box test"). - Le funzioni di test hanno firma esatta:
func TestXxx(t *testing.T)doveXxxinizia con maiuscola. - Si importa
testingdalla stdlib. - Niente
assertEquals: si usanoif got != want { t.Errorf(...) }confmt-verbs.
// math.go
package math
func Sum(a, b int) int { return a + b }
// math_test.go
package math
import "testing"
func TestSum(t *testing.T) {
got := Sum(2, 3)
if got != 5 {
t.Errorf("Sum(2,3) = %d; voglio %d", got, 5)
}
}Si esegue con:
go test ./... # tutti i package del modulo
go test -v ./pkg # verbose: stampa nomi test
go test -run TestSum # solo test che matchano il regext.Error vs t.Fatal
Sono i due verbi base per segnalare un fallimento:
| Metodo | Effetto |
|---|---|
t.Log(args...) | Logga ma non fallisce (visibile solo con -v o se fail). |
t.Error(args...) | Logga, marca FAIL, continua la funzione di test. |
t.Errorf(fmt, ...) | Come Error con formatting. |
t.Fatal(args...) | Logga, marca FAIL, interrompe il test (chiama runtime.Goexit). |
t.Fatalf(fmt, ...) | Come Fatal con formatting. |
Regola pratica: usa Fatal quando proseguire non ha senso (es. setup fallito, nil pointer dereference imminente); altrimenti Error, che permette di vedere tutti i fallimenti in una sola esecuzione.
func TestOpen(t *testing.T) {
f, err := os.Open("fixtures/sample.txt")
if err != nil {
t.Fatal(err) // se non si apre, gli step successivi falliscono per nil
}
defer f.Close()
// ... assert sul contenuto con t.Errorf
}Setup e teardown con t.Cleanup
Per ripulire risorse alla fine del test (o sub-test), usa t.Cleanup invece di defer:
func TestDB(t *testing.T) {
db := newTestDB(t)
t.Cleanup(func() { db.Close() })
// ... test
}Il vantaggio rispetto a defer: l'helper che crea db può registrare il cleanup internamente, così i chiamanti non devono ricordarsene.
Helper di test
I metodi t.Helper() marcano una funzione come "helper": quando un test fallisce, lo stack trace salta la funzione helper e punta direttamente al chiamante:
func mustParseInt(t *testing.T, s string) int {
t.Helper()
n, err := strconv.Atoi(s)
if err != nil {
t.Fatalf("parse %q: %v", s, err)
}
return n
}Esercizi
Definisci la funzione TestSum che verifica Sum(2,3) == 5 usando t.Errorf con un messaggio formattato.
Soluzione disponibile dopo 3 tentativi
In TestDiv, usa t.Fatal per interrompere immediatamente il test se Div ritorna un valore inatteso.
Soluzione disponibile dopo 3 tentativi
Qual è la differenza fondamentale tra t.Error e t.Fatal?
if err != nil {
t.???(err)
}