Vai al contenuto
eLearner.app
Modulo 9 · Lezione 1 di 541/50 nel corso~12 min
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.go e stanno nello stesso package del codice che testano (o in un package <nome>_test per "black-box test").
  • Le funzioni di test hanno firma esatta: func TestXxx(t *testing.T) dove Xxx inizia con maiuscola.
  • Si importa testing dalla stdlib.
  • Niente assertEquals: si usano if got != want { t.Errorf(...) } con fmt-verbs.
Go
// 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:

Bash
go test ./...        # tutti i package del modulo
go test -v ./pkg     # verbose: stampa nomi test
go test -run TestSum  # solo test che matchano il regex

t.Error vs t.Fatal

Sono i due verbi base per segnalare un fallimento:

MetodoEffetto
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.

Go
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:

Go
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:

Go
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

Esercizio#go.m9.l1.e1
Tentativi: 0Caricamento…

Definisci la funzione TestSum che verifica Sum(2,3) == 5 usando t.Errorf con un messaggio formattato.

Caricamento editor…

Soluzione disponibile dopo 3 tentativi

Esercizio#go.m9.l1.e2
Tentativi: 0Caricamento…

In TestDiv, usa t.Fatal per interrompere immediatamente il test se Div ritorna un valore inatteso.

Caricamento editor…

Soluzione disponibile dopo 3 tentativi

Quiz#go.m9.l1.e3
Pronto

Qual è la differenza fondamentale tra t.Error e t.Fatal?

Go
if err != nil {
  t.???(err)
}
Opzioni di risposta