web-dev-qa-db-fra.com

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?

62
ThePiachu

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()
}
76
Rob Napier

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.

21
Jacob Marble

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)
            }
        })
    }
}

Aller au terrain de je

3
Aleh

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

2
joonas.fi

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")

}

http://play.golang.org/p/ORWBqmPSVA

0
Thellimist