17370845950

Go测试中如何断言 Golang测试结果判断方式
Go测试中应优先用errors.Is匹配预定义错误、errors.As提取自定义错误类型,避免直接比较错误指针或断言错误字符串;断言错误时需覆盖成功与失败双路径,显式接收错误变量并用t.Fatal/t.Errorf明确报错。

Go测试中如何断言错误是否存在

最基础也最容易出错的一步:只检查 err != nil,却不处理成功路径的逻辑验证。很多测试看似通过,实则漏掉了“本该报错却没报”或“不该报错却报了”的关键场景。

  • ✅ 正确姿势:调用后立刻判断,失败时用 t.Fatalt.Errorf 明确指出预期与实际差异
  • ❌ 反模式:if err == nil { t.Fatal("expected error") } —— 语义绕、易读性差,且一旦 err 为 nil,后续逻辑可能被跳过导致误判
  • ⚠️ 注意:永远不要用 _, _ := fn() 忽略返回值;错误变量必须显式接收才能验证

errors.Is 用于匹配预定义错误值

当你导出类似 var ErrNotFound = errors.New("not found") 这样的包级错误变量时,errors.Is 是唯一安全可靠的匹配方式——它能穿透 fmt.Errorf("wrap: %w", err) 的包装链。

  • ✅ 推荐写法:if !errors.Is(err, ErrNotFound) { t.Errorf("expected ErrNotFound, got %v", err) }
  • ❌ 千万别写:err == ErrNotFound —— 一旦错误被包装,指针比较必然失败
  • ? 补充:若用 testify/assert,可简化为 assert.ErrorIs(t, err, ErrNotFound),语义更直白

errors.As 用于提取并断言自定义错误类型

当错误是结构体(比如 type ValidationError struct{ Field string }),你不能靠 err.(*ValidationError) 直接断言——包装过的错误会直接 panic 或返回 false。

  • ✅ 安全提取:var ve *ValidationError; if errors.As(err, &ve) { /* 使用 ve.Field */ }
  • ❌ 错误写法:if ve, ok := err.(*ValidationError); ok { ... } —— 对 fmt.Errorf("%w", ve) 完全失效
  • ⚠️ 注意:errors.As 第二个参数必须是指针地址(&ve),传值会导致匹配失败

错误消息断言:只在必要时才用,且要克制

校验 err.Error() 内容是最脆弱的断言方式。仅当错误文本本身是对外契约(如 CLI 工具的用户提示)时才考虑,否则优先用 errors.Iserrors.As

  • ✅ 可接受:if !strings.Contains(err.Error(), "timeout") { t.Error("missing timeout hint") }
  • ❌ 高危操作:err.Error() == "i/o timeout" —— 拼写、空格、标点微调就会让测试突然失败
  • ? 提示:Go 标准库几乎从不在文档中承诺错误字符串格式,所以把它当成实现细节而非接口

真正难的不是写对一个断言,而是理解错误在 Go 中是值、是链、是接口,不是字符串。很多人卡在 errors.As 传参传错地址,或在包装错误里还执着用类型断言,结果测得越勤,离真实行为越远。