recover必须在defer匿名函数中调用才有效,否则返回nil;测试中直接调用recover()永远得不到panic值,因panic已结束、调用栈已展开完毕。

recover 必须在 defer 匿名函数中调用,否则返回 nil
测试中直接写 recover() 永远得不到 panic 值,因为 panic 已结束、调用栈已展开完毕。只有在 defer func() { recover() }() 这种结构里,recover() 才可能捕获到值。
常见错误包括:
- 把
recover()写在panic()后面但不在 defer 里 - 写成
defer recover()—— 这会在注册时就执行,此时还没 panic - 在普通 if 分支或 return 前调用,完全不触发
正确姿势是:先注册 defer 匿名函数,再触发被测函数(它内部 panic),确保 defer 在 panic 前已入栈。
测试 panic 值需先判空再断言类型
recover() 返回 interface{},直接类型断言如 r.(error) 或 r.(string) 会 panic——如果实际 panic 的是整数或结构体,测试直接崩溃而非失败。
安全做法:
- 先用
assert.NotNil(t, r)确认捕获成功 - 再根据预期 panic 类型选择处理方式:
– 字符串消息:用assert.Equal(t, "expected", r)
– error 类型:用err, ok := r.(error); assert.True(t, ok); assert.Equal(t, "xxx", err.Error())
– 自定义结构体:确保可比较,用assert.Equal(t, expectedStruct, r)
子 goroutine 的 panic 无法被外层 recover 捕获
如果被测函数启动了 goroutine 并在里面 panic,主 goroutine 的 defer + recover() 完全无效。现象通常是测试卡住、超时,或进程直接崩溃且无堆栈输出。
解决路径只有两条:
- 重构被测逻辑,把 goroutine 内部行为抽成普通函数,测试时直接调用(去掉 goroutine)
- 若必须测并发场景,得在 goroutine 内部加自己的
defer func() { recover() }(),并配合 channel 或 sync.WaitGroup 等同步机制让测试可控
HTTP handler、worker pool 等真实场景务必注意这点——不能靠“外面包一层 defer”就以为万事大吉。
调试 recover 失效首选 runtime/debug.Stack()
当 recover() 返回 nil,但你确认 defer 已注册、panic 已触发,大概率是 panic 发生在别的 goroutine,或者 defer 函数自己又 panic 了。
这时别猜,直接在 defer 里加:
buf := make([]byte, 2048)
n := runtime.Stack(buf, false)
t.Log("stack trace:", string(buf[:n]))
打印完整堆栈,一眼定位 panic 真实发生位置和 goroutine ID。这是唯一可靠手段,比反复改 defer 顺序高效得多。
recover 不修复状态,也不跨协程,更不是兜底方案。测试里最易忽略的,是把 recover 当成“万能异常处理器”,而忘了它只解决“当前 goroutine 的 panic 是否被捕获”这一个问题。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











