assert 和 require 不能混用同一断言逻辑,因为 require 失败会 panic 终止执行,导致后续 assert 不运行;而 assert 仅记录错误并继续,二者语义与控制流截然不同。

为什么 assert 和 require 不能混用同一个断言逻辑
因为 require 遇到失败会直接 panic 并终止当前测试函数,而 assert 只记录错误、继续执行。如果你在 require.Equal(t, got, want) 后还写了依赖 got 非空的后续断言,一旦前面失败,后面代码根本不会跑——但你可能误以为“都执行了”,结果漏掉深层校验。
常见错误现象:test panicked: assertion failed: ... 后看不到后续断言的失败信息;或者测试看似通过,实则因前置 require 没触发而跳过了关键检查。
- 读取 JSON 响应后先
require.NoError(t, err),再用assert.NotNil(t, resp)—— 后者其实多余,require已保底 - 想验证结构体字段 + 方法行为?优先用
require确保对象存在,再用assert分项校验字段值 - HTTP handler 测试中,先
require.Equal(t, 200, rr.Code),再assert.JSONEq(t, expected, rr.Body.String())—— 这样分层合理
Mock 接口时 mock.Mock 的 On 调用顺序和参数匹配陷阱
On 方法注册的是“第一次匹配成功的调用规则”,不是“全局覆盖”。如果多个 On 注册了相同方法名但参数不同,Testify 会按注册顺序逐个比对,一旦匹配就返回对应值——不保证最精确匹配。
使用场景:模拟数据库查询、HTTP client、第三方 SDK 调用等需要控制返回值的依赖。
立即学习“go语言免费学习笔记(深入)”;
- 别写
mock.On("GetUser", mock.Anything).Return(user1, nil)再写mock.On("GetUser", 123).Return(user2, nil)—— 第一个就会拦截所有调用,第二个永远不会生效 - 要用
mock.AnythingOfType("*string")或自定义 matcher(如mock.MatchedBy(func(s string) bool { return s == "admin" }))来精准区分 - 忘记调用
mock.AssertExpectations(t)?测试会静默通过,即使实际没触发任何预期的On调用
assert.JSONEq 比 assert.Equal 多做了什么,又为什么有时反而更慢
assert.JSONEq 会先把两个字符串解析成 Go 的 map[string]interface{},再递归比较结构和值(忽略键序、空白、数字类型差异),而 assert.Equal 是纯字符串字面量对比。
性能影响明显:小 JSON(
- 接口返回固定格式且不含浮点数/时间戳时,用
assert.Equal+strings.TrimSpace更快也更可控 - 要容忍
{"id":1}和{"id":"1"}的语义等价?JSONEq不行,它仍判为不等;得自己预处理或换gjson校验关键路径 - 注意:如果输入不是合法 JSON,
JSONEq会 panic,而Equal不会 —— 所以先用assert.JSONSchema或简单json.Valid做前置检查更稳妥
在 TestMain 中集成 testify/suite 时为何 SetupTest 不生效
因为 testify/suite 的生命周期依赖 s.T() 返回的 *testing.T 实例,而 TestMain 是包级入口,绕过了 suite 自动注册的测试函数包装机制。直接在 TestMain 里调 s.SetupTest(),s.T() 还是 nil。
正确做法是放弃在 TestMain 中驱动 suite,改用标准的 func TestXxx(t *testing.T) 函数,并在其中调 suite.Run(t, &MySuite{})。
-
TestMain只适合做一次性的全局初始化(如启动临时 DB、设置环境变量),不要试图在里面运行 suite 生命周期 - 需要复用 setup/teardown?直接在 suite 结构体里实现
SetupTest/TearDownTest,然后确保每个测试函数都走suite.Run - 如果必须共享状态(比如一个共用的 mock server),把它声明为 suite 字段,在
SetupSuite启动、在TearDownSuite关闭,而不是塞进TestMain










