应根据类型是否必须一致选择:assert.equal要求类型完全匹配,assert.equalvalues只比较值而忽略底层类型;含未导出字段的结构体建议手动比较关键导出字段或实现string();错误断言必须用assert.erroras而非equal。

assert.Equal 和 assert.EqualValues 到底该用哪个
结构体或切片比较时,选错函数会导致断言意外失败。关键不是“哪个更好”,而是“类型是否必须一致”。
-
assert.Equal要求类型完全匹配:assert.Equal(t, []int{1,2}, []int64{1,2})一定失败,哪怕值一样 -
assert.EqualValues只比值,忽略底层类型:上例会通过,适合跨 JSON 解析(float64vsint)或数据库驱动返回类型不一致的场景 - 含未导出字段的结构体,
assert.Equal可能因反射权限 panic;assert.EqualValues同样受限,此时建议手动比关键导出字段,或实现String() - map 比较时两者都依赖 key/value 顺序无关性,但若 map 值是 slice 或 struct,仍需注意嵌套类型一致性
自定义错误必须用 assert.ErrorAs,不能用 assert.Equal
用 assert.Equal(t, err, &os.PathError{}) 永远失败——它只比 err.Error() 字符串,不识别包装、不递归 Unwrap(),更看不到底层类型和堆栈。
- 正确做法是:声明目标变量指针,再传给
assert.ErrorAs,例如var target *os.PathError; assert.ErrorAs(t, err, &target) -
target必须是非 nil 指针,否则断言直接 panic - 如果要检查错误是否属于某类(比如所有网络错误),用
errors.Is(err, net.ErrClosed)+assert.True,而不是靠字符串匹配
assert.NoError 和 require.NoError 的行为差异直接影响调试效率
区别不在“严格程度”,而在“后续代码是否还执行”。这个选择一旦错,会让 panic 堆栈指向错误位置,浪费大量排查时间。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
-
assert.NoError(t, err):失败时调用t.Logf打印错误,测试继续跑——适合并列验证多个独立字段(如 API 返回的 name/age/email) -
require.NoError(t, err):失败时调用t.Fatal,当前测试函数立刻终止——适用于前置依赖,比如配置加载、DB 连接、JSON 解析结果,后面代码若基于err == nil做操作,不用require就可能 panic -
require函数不返回 bool,不能写成if require.NoError(...) { },编译直接报错
表格驱动测试中不加 case 前缀,失败日志根本没法看
多个 t.Run 共享同一行 assert.Equal 时,失败只显示文件+行号,完全看不出是哪个输入 case 出问题。
- 最简方案:所有断言末尾显式加描述,例如
assert.Equal(t, tc.want, got, "case %q", tc.name) - 更稳妥的做法:确保
tc.name是唯一且有意义的字符串,并在t.Run(tc.name, ...)和断言消息里复用它,避免手误错配 - 空字符串或重复
tc.name会让 CI 日志失去定位能力,尤其当测试用例超过 20 个时,人工翻找成本陡增
最容易被忽略的是错误类型的断言方式——90% 的人第一次写自定义错误检查时都会下意识用 Equal,结果断言永远不通过,还花半天查是不是 error 生成逻辑有问题。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










