Testing 如何测试恐慌?
我目前正在考虑如何编写测试来检查给定代码是否惊慌失措?我知道Go用于捕捉恐慌,但与Java代码不同,您不能真正指定在恐慌情况下应该跳过哪些代码,或者您已经完成了哪些。如果我有一个函数:Testing 如何测试恐慌?,testing,go,Testing,Go,我目前正在考虑如何编写测试来检查给定代码是否惊慌失措?我知道Go用于捕捉恐慌,但与Java代码不同,您不能真正指定在恐慌情况下应该跳过哪些代码,或者您已经完成了哪些。如果我有一个函数: func f(t *testing.T) { defer func() { if r := recover(); r != nil { fmt.Println("Recovered in f", r) } }() OtherFunct
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")
}
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()
}
我真的不知道其他函数是否惊慌失措,我们是否恢复了,或者函数是否完全没有惊慌失措。如何指定在没有死机时跳过哪些代码,以及在死机时执行哪些代码?如何检查我们是否恢复了一些恐慌?您可以通过输入恐慌来测试哪个函数恐慌
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")
}
测试实际上没有“成功”的概念,只有失败。所以你上面的代码是正确的。你可能会发现这种风格稍微清晰一些,但基本上是一样的
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()
}
我通常发现测试
相当薄弱。您可能对更强大的测试引擎感兴趣,例如。即使您不想要完整的系统,也可以只使用它的matcher库,它可以与测试一起使用。Gomega包括以下匹配器:
Expect(OtherFunctionThatPanics).To(Panic())
您还可以将紧急检查总结为一个简单的函数:
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")
}
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()
}
当您需要检查panic的内容时,可以键入恢复的值:
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 TestOtherFunctionThatPanics(t *testing.T) {
assert.Panics(t, OtherFunctionThatPanics, "The code did not panic")
}
或者,如果您的otherfunctionthatparics
的签名不是func()
:
如果你还没试过作证,那也去看看吧。超级简单的断言和模拟。当在多个测试用例上循环时,我会选择如下方式:
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 f(t*testing.t){
已恢复:=func()(r bool){
延迟函数(){
如果r:=recover();r!=nil{
r=真
}
}()
OtherFunctionThatPanics()
//如果恐慌不被执行
// ....
}
如果!已恢复(){
t、 Errorf(“代码没有死机”)
//恐慌时被处决
// ....
}
}
作为一种通用紧急路由器功能,这也将起作用:
包恢复
func已恢复(IfPanic,Else func(),然后func(恢复接口{}))(恢复接口{}){
延迟函数(){
如果r:=recover();r!=nil{
{
//恐慌时被处决
如果那么!=零{
然后(r)
}
}
}
}()
IfPanic()
{
//如果恐慌不被执行
否则!=零{
延迟函数(){
recoverElse=recover()
}()
Else()
}
}
返回
}
var testError=errors.New(“预期错误”)
func TestRecover(t*testing.t){
恢复(
func(){
恐慌(测试错误)
},
func(){
t、 Errorf(“代码没有死机”)
},
func(r接口{}){
如果错误:=r.(错误);错误!=nil{
assert.Error(t、testError、err)
返回
}
t、 Errorf(“代码发生了意外的恐慌”)
},
)
}
惯用标准库解决方案
对我来说,下面的解决方案很容易阅读,并向维护人员展示了测试中的自然代码流。而且,它不需要第三方软件包
func TestPanic(t*testing.t){
//不需要检查'recover()'是否为零。只需关闭紧急状态。
延迟函数(){recover()}()
OtherFunctionThatPanics()
//如果“其他函数”恐慌,永远不要到达这里。
t、 Errorf(“没有惊慌”)
}
对于更通用的解决方案,您也可以这样做:
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 TestPanic(t*testing.t){
是否应使用PANIC(t,其他恐慌功能)
}
func shouldanic(t*testing.t,f func()){
延迟函数(){recover()}()
f()
t、 Errorf(“应该惊慌失措”)
}
以下是我的恐慌预期
func TestPanic(t*testing.t){
panicF:=func(){
//这里有紧急电话
}
需要。恐慌(t,恐慌)
}
对特定错误类型(如os.SyscallError)进行类型断言比比较错误消息(如从一个Go版本到下一个Go版本)更可靠。+Michael Aug,这可能是更好的方法,因为当有特定类型可用时。@IgorMikushkin在Go 1.11中,使用Rob Napier所描述的第一种形式实际上对覆盖范围有效;r==nil
,而不仅仅是recover()==nil
?@DuncanJones在这种情况下不是真的。这是一种非常典型的Go模式,可以使错误在块中可用,因此OP可能习惯于这样编写(我将他的代码提前了),但在本例中没有实际使用。