В настоящее время я размышляю, как написать тесты, которые проверяют, запаниковал ли данный фрагмент кода? Я знаю, что Go использует recover
для отлова паники, но, в отличие, скажем, от Java-кода, вы не можете точно указать, какой код следует пропускать в случае паники или что у вас есть. Итак, если у меня есть функция:
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")
}
Я не могу точно сказать, OtherFunctionThatPanics
запаниковали, и мы выздоровели, или функция вообще не паниковала. Как мне указать, какой код пропустить, если паники нет, и какой код выполнить в случае паники? Как я могу проверить, не оправились ли мы от паники?
r := recover(); r == nil
и не толькоrecover() == nil
?Если вы используете testify / assert , то это однострочный:
func TestOtherFunctionThatPanics(t *testing.T) { assert.Panics(t, OtherFunctionThatPanics, "The code did not panic") }
Или, если у вас
OtherFunctionThatPanics
есть подпись, отличная отfunc()
:func TestOtherFunctionThatPanics(t *testing.T) { assert.Panics(t, func() { OtherFunctionThatPanics(arg) }, "The code did not panic") }
Если вы еще не пробовали давать показания, то также ознакомьтесь с функцией «Свидетельствовать / издеваться» . Супер простые утверждения и насмешки.
источник
При прохождении нескольких тестовых примеров я бы выбрал что-то вроде этого:
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) } }) } }
Перейти на игровую площадку
источник
Когда вам нужно проверить содержимое паники, вы можете привести восстановленное значение:
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) }
Если код, который вы тестируете, не вызывает панику ИЛИ панику с ошибкой ИЛИ панику с сообщением об ошибке, которое вы ожидаете от него, тест завершится ошибкой (что вам и нужно).
источник
В вашем случае вы можете:
func f(t *testing.T) { recovered := func() (r bool) { defer func() { if r := recover(); r != nil { r = true } }() OtherFunctionThatPanics() // NOT BE EXECUTED IF PANICS // .... } if ! recovered() { t.Errorf("The code did not panic") // EXECUTED IF PANICS // .... } }
В качестве общей функции маршрутизатора паники это также будет работать:
https://github.com/7d4b9/recover
package recover func Recovered(IfPanic, Else func(), Then func(recover interface{})) (recoverElse interface{}) { defer func() { if r := recover(); r != nil { { // EXECUTED IF PANICS if Then != nil { Then(r) } } } }() IfPanic() { // NOT BE EXECUTED IF PANICS if Else != nil { defer func() { recoverElse = recover() }() Else() } } return } var testError = errors.New("expected error") func TestRecover(t *testing.T) { Recovered( func() { panic(testError) }, func() { t.Errorf("The code did not panic") }, func(r interface{}) { if err := r.(error); err != nil { assert.Error(t, testError, err) return } t.Errorf("The code did an unexpected panic") }, ) }
источник
Краткий путь
Для меня решение, приведенное ниже, легко читать и показывает естественный поток кода тестируемого кода.
func TestPanic(t *testing.T) { // No need to check whether `recover()` is nil. Just turn off the panic. defer func() { recover() }() OtherFunctionThatPanics() // Never reaches here if `OtherFunctionThatPanics` panics. t.Errorf("did not panic") }
Для более общего решения вы также можете сделать это так:
func TestPanic(t *testing.T) { shouldPanic(t, OtherFunctionThatPanics) } func shouldPanic(t *testing.T, f func()) { defer func() { recover() }() f() t.Errorf("should have panicked") }
источник
Вы можете проверить, какая функция вызывает панику, введя панику
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
источник