errors.is用于判断错误是否等于特定预定义错误变量,能穿透%w包装;errors.as用于提取自定义错误类型;禁用字符串匹配和反射判断。

如何用 errors.Is 判断返回错误是否为某一个特定错误
Go 1.13 引入的 errors.Is 是检查错误是否等于某个已知错误(比如包级变量错误)的首选方式,它能穿透 fmt.Errorf("...: %w", err) 的包装链。不用它,直接用 == 会漏掉被包装过的错误。
常见错误现象:测试里写 if err == io.EOF,但实际返回的是 fmt.Errorf("read failed: %w", io.EOF),结果判断失败。
-
errors.Is(err, io.EOF)能正确返回true - 只对预定义的错误变量(如
io.EOF、os.ErrNotExist)或你自己定义的包级var ErrNotFound = errors.New("not found")有效 - 别对
errors.New("xxx")的临时值做errors.Is判断——每次调用都生成新地址,永远不相等
怎么写测试断言来验证自定义错误类型(比如 MyError)
如果函数返回的是实现了 error 接口的结构体(比如 type MyError struct{ Code int }),就得用 errors.As 来提取底层错误值,而不是靠字符串匹配或 errors.Is。
使用场景:API 返回带状态码的错误,你想在测试里确认它确实是 MyError 类型,并检查其字段。
立即学习“go语言免费学习笔记(深入)”;
- 写法是
var e *MyError; if errors.As(err, &e) { ... } - 注意传的是指针地址
&e,不是e;否则errors.As无法赋值 - 如果错误是
fmt.Errorf("wrap: %w", &MyError{Code: 404}),errors.As依然能穿透找到*MyError - 别用
reflect.TypeOf(err).Name()做类型判断——不可靠,且绕过错误包装语义
为什么不要用 strings.Contains(err.Error(), "xxx") 做错误校验
字符串匹配看着快,但极其脆弱:日志格式一改、加个前缀、翻译成其他语言,测试就挂。它还完全无视错误的语义和包装关系。
性能影响不大,但可维护性归零。Go 官方文档明确反对这种写法。
- 一旦错误消息里出现用户输入(比如文件名),
err.Error()可能含空格、引号、换行,strings.Contains会误判 - 多个错误共用相似关键词(比如 “timeout” 出现在
context.DeadlineExceeded和你自己的ErrTimeout里),容易假阳性 - 如果错误被多次包装,
err.Error()只返回最外层消息,丢失原始错误上下文
测试中遇到 nil 错误却仍要检查类型怎么办
有些函数在成功时返回 nil,但测试逻辑需要确认“没出错”这件事本身是否符合预期。这时候重点不是检查错误内容,而是确保它确实是 nil。
容易踩的坑是:写了 if err != nil { t.Fatal(err) } 就以为万事大吉,但没覆盖“本该返回非 nil 却返回了 nil”的反向 case。
- 正向断言:用
if err != nil { t.Fatalf("expected nil error, got %v", err) } - 反向断言(比如测试错误路径):先确保
err != nil,再用errors.Is或errors.As继续深挖 - 别写
assert.Nil(t, err)这类第三方断言——标准库足够,且更易调试(失败时直接打出 err 值)
真正麻烦的是嵌套多层的错误链,尤其是跨 package 包装时。建议从第一层错误开始用 errors.Is 或 errors.As,别试图手动递归解包;Go 的错误处理设计就是让你少碰 err.Unwrap()。










