2015-03-06 9 views
6

Consideriamo questo semplice codice di test.Come risolvere i numeri di riga nell'output di test Go?

(Nota:. assertSomething è super semplice qui, ma di solito mi piacerebbe scrivere un aiuto più specializzato per il compito a portata di mano che guardare le cose più e potrebbe segnalare più di un tipo di errore)

package hello 

import "testing" 

func TestFoo(t *testing.T) { 
    assertSomething(t, 2+2 == 4) // line 6 
    assertSomething(t, 2+3 == 6) // line 7 
} 

func assertSomething(t *testing.T, expected bool) { 
    if !expected { 
     t.Error("Something's not right") // line 12 
    } 
} 

Quando eseguo go test, ricevo il seguente:

--- FAIL: TestFoo (0.00s) 
    hello.go:12: Something's not right 
FAIL 
exit status 1 
FAIL kos/hello 0.008s 

ho due domande:

1) L'errore punta alla riga 12 - perché? In che modo t.Error individua da quale riga è stato chiamato?

2) l'aiutante, vorrei precisare che t.Error dovrebbe guardare livello di strato superiore per determinare il numero di riga da stampare, in modo che avrei ricevuto un messaggio come questo:

--- FAIL: TestFoo (0.00s) 
    hello.go:7: Something's not right 

Python permette per fare questo, ad esempio, in warnings.warn("message", stacklevel=2) - come implementerei l'equivalente qui?

+0

Per quanto concerne 1, è probabile che utilizza [runtime.Caller] (http://godoc.org/runtime#Caller) per questo scopo. Fornisci un numero diverso per "salta" per salire di alcuni livelli. Non sono sicuro che tu possa dire a 'test.XXX' di farlo senza modificare il codice del pacchetto di test. – jimt

risposta

12

È può fare quello che stai chiedendo, e si può scoprire come t.Error opere looking at the source code. La funzione decorate è ciò che stai cercando, penso.

Ma, nel caso in cui si abbia una quantità significativa di codice di controllo, e per qualche motivo si stia duplicando nel test, è meglio estrarlo come una funzione che restituisce un errore piuttosto che passare un test.T e fare è una "asserzione". Infatti, la scrittura delle funzioni di asserzione è esplicitamente scoraggiata nella lingua FAQ.

package hello 

import "testing" 

func TestFoo(t *testing.T) { 
    if err := checkSomething(2+2 == 4); err != nil { 
     t.Errorf("2+2=4 failed: %s", err) 
    } 
    if err := checkSomething(2+3 == 6); err != nil { 
     t.Errorf("2+3=6 failed: %s", err) 
    } 
} 

func checkSomething(v bool) error { 
    if !v { 
     return errors.New("something's not right") 
    } 
    return nil 
} 

Ma qui è quello che penso codice di test idiomatica sarà simile. È basato su tabelle e i casi includono input e output previsti, che portano a messaggi di errore veramente chiari quando i test falliscono.

package hello 

import "testing" 

func TestFoo(t *testing.T) { 
    cases := []struct { 
     a, b, want int 
    }{ 
     {2, 2, 4}, 
     {2, 3, 6}, 
    } 
    for _, c := range cases { 
     if got := operation(c.a, c.b); got != c.want { 
      t.Errorf("operation(%d, %d) = %d, want %d", c.a, c.b, got, c.want) 
     } 
    } 
} 

func operation(a, b int) int { 
    return a + b 
} 
+0

Wow, grazie. E per testare il codice async stateful, c'è un approccio più idiomatico di "mettere qualcosa in un canale, affermare che c'è qualcosa su un altro canale, ripetere"? Qualche esempio a cui posso fare riferimento? Non penso di poter scrivere su quel tavolo. – Kos

+0

Purtroppo, gli helper di test più complessi (non le asserzioni) sono utili e devono regolare il livello di chiamata per esattamente i motivi documentati in quella voce di domande frequenti: se eseguono più controlli, dovrebbero essere in grado di aggiungere più errori. La magia per aggiungere il sito di chiamata è purtroppo sepolta in un helper non esportato (il metodo di decoro), quindi aggiungere questa funzione richiede una buona dose di duplicazione o una libreria di test alternativa. Ad esempio, è possibile ripetere la stessa serie di test su diversi oggetti che implementano un'interfaccia – stub

Problemi correlati