assert/testify 比 if + t.error 更好用,因其自动记录位置、输出上下文、不中断执行;require 用于前置条件校验并立即终止,避免非法状态后续执行。

testify/assert 为什么比 if + t.Error 更好用
因为 assert 能自动记录失败位置、输出上下文、不中断执行,而手动写 if !reflect.DeepEqual(got, want) { t.Errorf(...) } 容易漏掉错误信息格式、忘记加行号、一错就停——尤其在循环或批量校验时,根本看不出哪次迭代挂了。
常见错误现象:t.Error 后继续跑,但没打日志;t.Fatal 一错就停,后续 case 全被跳过;自己拼字符串容易少空格、缺换行,diff 看不清。
-
assert.Equal(t, got, want)失败只报错,测试继续 -
require.Equal(t, got, want)失败立刻终止当前函数(适合前置条件) - 所有
assert.Xxx都自带文件名+行号,不用手写t.Errorf("line %d: ...", runtime.Caller(1)) - 对 slice/map/struct 做深度比较,不用再 import
reflect或写递归逻辑
assert 和 require 的关键区别:什么时候该用哪个
不是风格偏好问题,是控制流设计问题。require 不是“更强的 assert”,而是“带 panic 的断言”——它让测试函数提前 return,避免后续代码在非法状态下运行。
- 用
require.NoError(t, err)检查初始化或 setup 错误,比如json.Unmarshal失败后还去 assert 字段就毫无意义 - 用
assert.Len(t, list, 3)校验业务逻辑结果,即使长度不对,也值得看后面字段是否符合预期 - require 在 defer 里无效(panic 被 recover),所以别在 defer 中调用
require.Xxx - require 的 panic 不会触发
testing.T.Cleanup,如果 cleanup 里有资源释放逻辑,得确保它不依赖 require 成功
常见类型断言的写法和坑点(map、error、nil、float)
Go 类型系统严格,但 assert 默认行为有时反直觉,比如 nil 判断、浮点误差、自定义 error 实现。
-
assert.Nil(t, err)和assert.NotNil(t, val)只认接口零值,err == nil才过;如果 err 是自定义 struct 指针且非空但Error() == "",它仍算非 nil -
assert.Equal(t, got, want)对 float64 不做误差容忍,要用assert.InDelta(t, got, want, 0.001) -
assert.Equal(t, map[string]int{"a": 1}, map[string]int{"a": 1})可以,但assert.Equal(t, &T{}, &T{})会失败(指针地址不同),应改用assert.True(t, reflect.DeepEqual(got, want))或直接用require.Equal+ struct 值比较 -
assert.ErrorContains(t, err, "timeout")是 v1.8+ 新增,老版本只能用assert.Contains(t, err.Error(), "timeout"),注意 err 为 nil 时err.Error()panic
性能与兼容性:引入 testify 会不会拖慢测试或引发冲突
不会显著影响执行速度,但要注意 vendor 和 go.mod 版本收敛。testify/assert 是纯函数库,无 goroutine、无全局状态、无 init 副作用。
- 编译期零开销:所有断言都是普通函数调用,没有反射扫描或代码生成
- go 1.16+ 支持
//go:build test,可把 testify 限定在 test 文件中,主模块不依赖 - 多个子模块共用 testify 时,若版本不一致(如 v1.7 vs v1.8),
go mod tidy通常能自动升到高版本,但assert.ErrorAs在 v1.7 中行为有 bug(可能误判 interface{}),建议锁死 v1.8.4+ - 别在
init()函数里用任何 testify 断言——测试框架未启动,t为空指针,直接 panic
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











