答案:在Go测试中,通过defer和recover捕获panic,可验证函数在异常情况下是否按预期触发panic并检查其值。利用辅助函数如assertPanics可封装重复逻辑,提升测试复用性与可读性;对recover返回的interface{}进行类型断言,可精细化验证panic的类型和内容,确保程序在非法输入或严重错误时以可预测方式终止,从而保障代码鲁棒性。

在Go语言的测试中,捕获并断言
panic
panic
defer
recover
panic
当我们需要测试一个函数在特定条件下是否会触发
panic
panic
defer
recover
defer
recover()
panic
recover()
panic
举个例子,假设我们有一个函数
divide
panic
package main
import (
"fmt"
)
func divide(a, b int) int {
if b == 0 {
panic("division by zero is not allowed")
}
return a / b
}
// 假设这是我们的测试文件 (e.g., my_test.go)
// import "testing"
// func TestDivideByZeroPanics(t *testing.T) {
// defer func() {
// if r := recover(); r == nil {
// t.Errorf("The code did not panic when it should have")
// } else if r != "division by zero is not allowed" {
// t.Errorf("Panicked with unexpected message: %v", r)
// }
// }()
// divide(10, 0) // 这行代码会触发panic
// t.Errorf("Function did not stop execution after panic") // 这行不应该被执行到
// }在上面的测试代码中,
defer
TestDivideByZeroPanics
divide(10, 0)
panic
defer
recover()
panic
"division by zero is not allowed"
recover()
nil
panic
立即学习“go语言免费学习笔记(深入)”;
说实话,刚开始接触
panic
panic
panic
最常见的场景是,当一个函数接收到完全非法或无法处理的输入时,它可能会选择
panic
error
nil
nil
panic
panic
panic
error
panic
panic
简而言之,测试中捕获
panic
每次都写一长串
defer
panic
一个常见的模式是创建一个
assertPanics
expectPanic
*testing.T
panic
package main
import (
"fmt"
"testing"
)
func divide(a, b int) int {
if b == 0 {
panic("division by zero is not allowed")
}
return a / b
}
// assertPanics 是一个测试辅助函数,用于断言传入的函数会发生panic
// 它返回panic的值,如果未发生panic则返回nil
func assertPanics(t *testing.T, f func()) (recovered interface{}) {
defer func() {
recovered = recover()
}()
f() // 执行传入的函数
return // 如果f()没有panic,recovered将是nil
}
func TestDivideByZeroPanicsRefactored(t *testing.T) {
// 期望的panic消息
expectedPanicMsg := "division by zero is not allowed"
// 使用辅助函数捕获panic
r := assertPanics(t, func() {
divide(10, 0)
})
if r == nil {
t.Errorf("The code did not panic when it should have")
} else if msg, ok := r.(string); !ok || msg != expectedPanicMsg {
t.Errorf("Panicked with unexpected value: %v, expected: %q", r, expectedPanicMsg)
}
}
func TestNoPanicWhenNotExpected(t *testing.T) {
r := assertPanics(t, func() {
divide(10, 2) // 不会panic
})
if r != nil {
t.Errorf("The code panicked unexpectedly with: %v", r)
}
}通过
assertPanics
panic
panic
recover()
interface{}panic
interface{}panic
panic
以下是几种常见的精细化断言方式:
检查panic
panic
// ... (assertPanics 辅助函数同上)
func TestSpecificStringPanic(t *testing.T) {
expectedMsg := "something went terribly wrong"
r := assertPanics(t, func() {
panic(expectedMsg)
})
if r == nil {
t.Errorf("Expected panic, but got none.")
} else if msg, ok := r.(string); !ok || msg != expectedMsg {
t.Errorf("Panicked with unexpected message or type: got %v, expected string %q", r, expectedMsg)
}
}检查panic
error
errors.New
error
panic
// ... (assertPanics 辅助函数同上)
type MyCustomError struct {
Code int
Msg string
}
func (e MyCustomError) Error() string {
return fmt.Sprintf("Error %d: %s", e.Code, e.Msg)
}
func functionPanickingWithError() {
panic(fmt.Errorf("an underlying error occurred"))
}
func functionPanickingWithCustomError() {
panic(MyCustomError{Code: 500, Msg: "Internal server issue"})
}
func TestPanicWithErrorType(t *testing.T) {
r := assertPanics(t, functionPanickingWithError)
if r == nil {
t.Errorf("Expected panic, but got none.")
} else if err, ok := r.(error); !ok || err.Error() != "an underlying error occurred" {
t.Errorf("Panicked with unexpected error or message: got %v, expected error 'an underlying error occurred'", r)
}
}
func TestPanicWithCustomErrorType(t *testing.T) {
r := assertPanics(t, functionPanickingWithCustomError)
if r == nil {
t.Errorf("Expected panic, but got none.")
} else if customErr, ok := r.(MyCustomError); !ok {
t.Errorf("Panicked with unexpected type: got %T, expected MyCustomError", r)
} else if customErr.Code != 500 || customErr.Msg != "Internal server issue" {
t.Errorf("Panicked with unexpected custom error details: got %+v", customErr)
}
}检查panic
panic
error
// ... (assertPanics 辅助函数同上)
type PanicContext struct {
Component string
Reason string
}
func functionPanickingWithContext() {
panic(PanicContext{Component: "DB", Reason: "Connection lost"})
}
func TestPanicWithStruct(t *testing.T) {
r := assertPanics(t, functionPanickingWithContext)
if r == nil {
t.Errorf("Expected panic, but got none.")
} else if ctx, ok := r.(PanicContext); !ok {
t.Errorf("Panicked with unexpected type: got %T, expected PanicContext", r)
} else if ctx.Component != "DB" || ctx.Reason != "Connection lost" {
t.Errorf("Panicked with unexpected context details: got %+v", ctx)
}
}精细化断言能够确保我们的测试不仅仅是“它
panic
panic
panic
panic
panic
panic
以上就是Golang测试中捕获panic并断言处理的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号