应使用 fmt.Errorf("wrap: %w", err) 包装错误以支持 errors.Is 和 errors.As 断言,避免 errors.New 字符串比较;依赖接口抽象与 mock 实现可控错误注入;panic 测试需 defer+recover 捕获;严禁 os.Exit 或未重定向的 log.Fatal。

如何用 errors.New 和 fmt.Errorf 构造可测试的错误
构造可控、可识别的错误是错误场景测试的前提。直接用 errors.New("something failed") 创建的错误无法携带上下文,也不便于断言;而 fmt.Errorf("failed: %w", err) 支持包装(%w),能保留原始错误链,方便后续用 errors.Is 或 errors.As 判断。
实操建议:
- 测试中需断言具体错误类型时,优先用
fmt.Errorf("wrap: %w", originalErr)包装,而非拼接字符串 - 自定义错误类型(如实现
Error()方法)更利于精准匹配,尤其在需要区分不同业务错误码时 - 避免用
==比较两个errors.New的结果——即使消息相同,它们也是不同实例,比较结果恒为false
用 testify/mock 或接口替换模拟失败依赖
Golang 本身不支持运行时方法打桩,所以“让某个 HTTP 调用返回 500”或“让数据库查询 panic”必须靠提前设计:把外部依赖抽象为接口,测试时注入模拟实现。
常见做法:
立即学习“go语言免费学习笔记(深入)”;
- 定义
type PaymentClient interface { Charge(ctx context.Context, req *ChargeReq) (*ChargeResp, error) },生产代码依赖该接口,而非具体 HTTP 客户端 - 测试中实现该接口的 mock 版本,在
Charge方法里直接return nil, errors.New("network timeout") - 若用
testify/mock,注意调用mock.On("Charge", mock.Anything, mock.Anything).Return(nil, errors.New("timeout"))后,必须调用mock.AssertExpectations(t)验证是否被按预期调用
测试 panic 场景要用 recover 捕获,不能只靠 go test
当函数内部有 panic("invalid input"),直接调用会中断测试流程。必须手动启动 goroutine + recover 才能捕获并验证 panic 内容。
正确写法示例:
func TestProcessPanic(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatal("expected panic")
} else if r != "invalid input" {
t.Fatalf("unexpected panic: %v", r)
}
}()
Process("bad")
}
注意点:
-
recover()只在 defer 中有效,且仅捕获当前 goroutine 的 panic - 不要在测试函数里用
os.Exit(1)替代 panic——它会彻底终止进程,go test无法捕获或继续执行其他用例 - 如果被测函数已用
log.Fatal,需先重定向log.SetOutput(ioutil.Discard),否则测试会卡住
errors.Is 和 errors.As 是断言错误本质的核心工具
很多开发者仍用 strings.Contains(err.Error(), "not found") 做判断,这脆弱且易误判。Golang 1.13+ 推荐用 errors.Is(匹配哨兵错误)和 errors.As(提取错误类型)。
典型用法:
- 定义哨兵错误:
var ErrNotFound = errors.New("record not found"),在业务逻辑中返回return nil, ErrNotFound - 测试中写
if errors.Is(err, ErrNotFound) { ... },不依赖错误消息文本 - 若错误是自定义结构体(如
type ValidationError struct{ Field string }),用var ve *ValidationError; if errors.As(err, &ve) { t.Log(ve.Field) }
容易忽略的是:只有用 %w 包装的错误才能被 errors.Is / errors.As 向下穿透查找;用 %s 或 + " 拼接的错误会切断错误链。










