断言必须先求值再调用,避免panic吞掉上下文;HTTP测试需先解包resp.Body再断言;JSON响应必须用assert.JSONEq并前置json.Valid校验;动态字段需预处理;require.NoError用于前置守门,assert.NoError用于并列验证;每个t.Run需新建assert实例确保行号准确。

testify/assert 必须先求值再断言,否则 panic 会吞掉失败上下文
直接在 assert.Equal 参数里调用可能出错的函数(比如 json.Unmarshal、HTTP 请求、DB 查询),一旦该函数 panic,测试进程直接崩溃,assert 根本没机会执行,你只会看到一条模糊的 panic 堆栈,完全不知道是哪个用例、哪行逻辑崩了。
- 错误写法:
assert.Equal(t, "ok", riskyFunc()) - 正确写法:先赋值,再断言
got := riskyFunc()assert.Equal(t, "ok", got) - 对 HTTP handler 测试尤其关键:必须先解包
resp.Body成字节切片,再转字符串或 JSON,不能把io.ReadAll(resp.Body)塞进assert.JSONEq参数里 - 如果确实需要链式调用(如
json.Marshal(v).([]byte)),加defer func() { if r := recover(); r != nil { t.Fatalf("panic during marshal: %v", r) } }()捕获并转为明确失败
JSON 响应必须用 assert.JSONEq,字符串相等断言在微服务里几乎总是错的
assert.Equal(t, `{"id":1,"name":"foo"}`, string(body)) 看似直观,但只要字段顺序一变、float 多一位小数、多一个空格,就失败。这不是“不稳定”,而是设计上就不该这么比。
-
assert.JSONEq会先调用json.Unmarshal解析两边,再按语义比较键值对,忽略格式、空格、键序 - 前提是输入必须是合法 JSON;非法 JSON 会导致 panic,所以要在
assert.JSONEq前加assert.True(t, json.Valid(body))或手动校验 - 含动态字段(如
"created_at"、"uuid")时,先用strings.ReplaceAll或正则抹掉,再喂给assert.JSONEq;不要试图在 JSON 字符串里写"created_at": "*"这类模糊匹配 - 别用
assert.EqualValues替代 —— 它不解析 JSON,只是做反射比较,照样会被格式差异击穿
require.NoError 是前置守门员,assert.NoError 是事后检查员
二者不是“强弱”区别,而是控制流分叉点:后续逻辑是否依赖当前值有效。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 用
require.NoError的典型位置:json.Unmarshal(body, &resp)后、repo.GetUser(id)返回后、配置加载后——一旦失败,resp或user是零值,再访问resp.Data或user.Name要么 panic,要么断言假阳性 -
assert.NoError适合并列验证多个独立字段,比如同时检查 API 返回的Name、Email、Age,一次看到所有问题 -
require.NoError不返回 bool,不能写成if require.NoError(t, err) { ... },编译不过 - 别在
defer里调用require.Xxx——defer里的 panic 会被 runtime recover,断言失效且无日志
表驱动测试里每个子测试必须 new 一个 assert 实例
复用同一个 *assert.Assertions 实例会导致失败行号指向外层循环,而不是真实出错的 t.Run 内部,排查成本翻倍。
立即学习“go语言免费学习笔记(深入)”;
- ❌ 错误:
a := assert.New(t)放在for循环外,所有t.Run共享一个a - ✅ 正确:在每个
t.Run函数体内调用assert.New(t),确保上下文隔离 - 更稳妥的做法是把测试用例名带进断言消息:
assert.Equal(t, tc.want, got, "case %q", tc.name),失败时一眼定位到具体 case - 创建
*assert.Assertions实例开销可忽略,远小于一次 map 查找或 HTTP round trip
ErrorAs)防止错误包装被忽略——这三者漏掉任何一个,测试都可能变成“看起来过了,其实没过”。


















