testing - serp - ¿Cómo probar el pánico?
test seo tags (5)
Actualmente estoy reflexionando sobre cómo escribir pruebas que verifiquen si una determinada pieza de código entró en pánico.
Sé que Go usa la
recover
para atrapar los pánicos, pero a diferencia de, por ejemplo, el código Java, no se puede especificar qué código se debe omitir en caso de pánico o qué tiene.
Entonces, si tengo una función:
func f(t *testing.T) {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
OtherFunctionThatPanics()
t.Errorf("The code did not panic")
}
Realmente no puedo decir si
OtherFunctionThatPanics
entró en pánico y nos recuperamos, o si la función no entró en pánico en absoluto.
¿Cómo especifico qué código omitir si no hay pánico y qué código ejecutar si hay pánico?
¿Cómo puedo verificar si hubo algo de pánico del que nos recuperamos?
Al recorrer múltiples casos de prueba, iría por algo como esto:
func TestIsAheadComparedToPanicsWithDifferingStreams(t *testing.T) {
defer func() {
err := recover().(error)
if err.Error() != "Cursor: cannot compare cursors from different streams" {
t.Fatalf("Wrong panic message: %s", err.Error())
}
}()
c1 := CursorFromserializedMust("/foo:0:0")
c2 := CursorFromserializedMust("/bar:0:0")
// must panic
c1.IsAheadComparedTo(c2)
}
Cuando necesite verificar el contenido del pánico, puede escribir el valor recuperado:
package main
import (
"reflect"
"testing"
)
func TestYourFunc(t *testing.T) {
type args struct {
arg1 int
arg2 int
arg3 int
}
tests := []struct {
name string
args args
want []int
wantErr bool
wantPanic bool
}{
//TODO: write test cases
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer func() {
r := recover()
if (r != nil) != tt.wantPanic {
t.Errorf("SequenceInt() recover = %v, wantPanic = %v", r, tt.wantPanic)
}
}()
got, err := YourFunc(tt.args.arg1, tt.args.arg2, tt.args.arg3)
if (err != nil) != tt.wantErr {
t.Errorf("YourFunc() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("YourFunc() = %v, want %v", got, tt.want)
}
})
}
}
Si el código que está probando no entra en pánico O entra en pánico con un error O entra en pánico con el mensaje de error que espera, la prueba fallará (que es lo que desea).
Puede probar qué función en pánico dando una entrada al pánico
package main
import "fmt"
func explode() {
// Cause a panic.
panic("WRONG")
}
func explode1() {
// Cause a panic.
panic("WRONG1")
}
func main() {
// Handle errors in defer func with recover.
defer func() {
if r := recover(); r != nil {
var ok bool
err, ok := r.(error)
if !ok {
err = fmt.Errorf("pkg: %v", r)
fmt.Println(err)
}
}
}()
// These causes an error. change between these
explode()
//explode1()
fmt.Println("Everything fine")
}
Si usa testify/assert , entonces es una línea:
func TestOtherFunctionThatPanics(t *testing.T) {
assert.Panics(t, OtherFunctionThatPanics, "The code did not panic")
}
O, si su
OtherFunctionThatPanics
tiene una firma distinta de
func()
:
func TestOtherFunctionThatPanics(t *testing.T) {
assert.Panics(t, func() { OtherFunctionThatPanics(arg) }, "The code did not panic")
}
Si aún no has probado testificar, también echa un vistazo a testify/mock . Súper simples afirmaciones y simulacros.
testing
realmente no tienen el concepto de "éxito", solo fracaso.
Entonces su código anterior es correcto.
Puede encontrar este estilo un poco más claro, pero es básicamente lo mismo.
func TestPanic(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Errorf("The code did not panic")
}
}()
// The following is the code under test
OtherFunctionThatPanics()
}
En general, las
testing
son bastante débiles.
Puede que le interesen motores de prueba más potentes como
Ginkgo
.
Incluso si no desea el sistema Ginkgo completo, puede usar solo su biblioteca de
Gomega
,
Gomega
, que puede usarse junto con las
testing
.
Gomega incluye matchers como:
Expect(OtherFunctionThatPanics).To(Panic())
También puede resumir la comprobación de pánico en una función simple:
func TestPanic(t *testing.T) {
assertPanic(t, OtherFunctionThatPanics)
}
func assertPanic(t *testing.T, f func()) {
defer func() {
if r := recover(); r == nil {
t.Errorf("The code did not panic")
}
}()
f()
}