Comment tester les paniques?
Je me demande actuellement comment écrire des tests qui vérifient si un morceau de code donné a paniqué? Je sais que Go utilise recover
pour attraper les paniques, mais contrairement à disons, Java code, vous ne pouvez pas vraiment spécifier le code à sauter) en cas de panique ou quoi d'autre. Donc si j'ai une fonction:
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")
}
Je ne peux pas vraiment dire si OtherFunctionThatPanics
a paniqué et nous avons récupéré, ou si la fonction n'a pas paniqué du tout. Comment spécifier quel code ignorer s'il n'y a pas de panique et quel code exécuter s'il y a une panique? Comment puis-je vérifier s'il y a eu une panique dont nous nous sommes remis?
testing
n'a pas vraiment le concept de "succès", seulement d'échec. Donc, votre code ci-dessus est à peu près correct. Vous pourriez trouver ce style un peu plus clair, mais c'est essentiellement la même chose.
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()
}
Je trouve généralement que testing
est assez faible. Vous pourriez être intéressé par des moteurs de test plus puissants comme Ginkgo . Même si vous ne voulez pas le système Ginkgo complet, vous pouvez utiliser uniquement sa bibliothèque de matcher, Gomega , qui peut être utilisée avec testing
. Gomega comprend des matchers comme:
Expect(OtherFunctionThatPanics).To(Panic())
Vous pouvez également conclure la vérification de panique dans une fonction 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()
}
Si vous utilisez testify/assert , alors c'est une ligne:
func TestOtherFunctionThatPanics(t *testing.T) {
assert.Panics(t, OtherFunctionThatPanics, "The code did not panic")
}
Ou, si votre OtherFunctionThatPanics
a une signature autre que func()
:
func TestOtherFunctionThatPanics(t *testing.T) {
assert.Panics(t, func() { OtherFunctionThatPanics(arg) }, "The code did not panic")
}
Si vous n'avez pas encore essayé de témoigner, consultez également testify/mock . Affirmations et simulations super simples.
En bouclant sur plusieurs cas de test, j'opterais pour quelque chose comme ceci:
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)
}
})
}
}
Lorsque vous devez vérifier le contenu de la panique, vous pouvez transtyper la valeur récupérée:
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)
}
Si le code que vous testez ne panique pas OR panique avec une erreur OR panique avec le message d'erreur auquel vous vous attendez, le test échouera (qui est ce que vous voudriez).
Vous pouvez tester la fonction paniquée en donnant une entrée de panique
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")
}