应优先使用 require 而非 assert 的核心判断标准是:后续断言是否依赖当前值有效;require 用于阻止非法状态传播,assert 用于独立验证,且每个子测试需创建独立的 *assert.Assertions 实例。

assert.Equal 和 require.Equal 的行为差异直接决定测试是否可靠,选错会导致 panic 掩盖真实断言失败,或关键错误被忽略后继续执行。
什么时候该用 require 而不是 assert
核心判断标准:后续断言是否依赖当前值有效。
-
require.NoError(t, err)必须放在调用函数后第一行——如果 err 不为 nil,后续对result.Name、user.ID的任何访问都可能 panic,而 panic 堆栈会指向那行字段访问,不是真正的错误源头 -
require.NotNil(t, resp)适合 HTTP 客户端返回值检查,避免在resp.StatusCode上 panic -
require.Len(t, items, 3)后再用assert.Equal检查每个元素,比在items[0]上裸奔安全得多 - 不能在
defer中调用require.Xxx,panic 会被 recover,导致断言失效且无日志
表驱动测试中 assert.New(t) 的正确写法
每个 t.Run 子测试必须拥有独立的 *assert.Assertions 实例,否则失败行号错乱、日志混杂。
- ❌ 错误:在循环外创建
a := assert.New(t),然后在所有t.Run里复用 —— 所有子测试共享同一个t,失败时显示的是最外层测试文件行号 - ✅ 正确:在
t.Run函数体内调用assert.New(t),确保上下文隔离 - 更稳妥的做法是把
tc.name同时用作t.Run名称和断言消息前缀,例如:assert.Equal(t, tc.want, got, "case %q", tc.name) - 不需要担心性能:创建
assert.Assertions实例开销可忽略,远小于一次 map 查找
assert.Equal 对 float、map、error 的常见陷阱
默认 Equal 是严格反射比较,不处理浮点误差、map 无序性、错误包装链。
- 浮点数比较必须用
assert.InDelta(t, got, want, 0.001),Equal会因精度丢失直接失败 - 两个内容相同但 key 插入顺序不同的
map[string]int,Equal可能失败(Go runtime 不保证迭代顺序),此时应改用assert.ElementsMatch或先排序 key - 自定义 error 类型不能用
assert.Equal(t, err, &MyError{}),它只比Error()字符串;要确认类型,必须用assert.ErrorAs(t, err, &target),且target必须是非 nil 指针 -
assert.EqualValues只适用于“语义等价”场景(如[]int{1}vs[]int64{1}),但它对未导出字段或含函数字段的结构体同样会失败
require 切断非法状态的传播,用独立 assert 实例守住每个子测试边界,再配合类型敏感的断言变体——这三者缺一不可。


















