研发协作
用 testify 写 Go 测试时如何正确选择…
当 Go 代码库导入 github.com/stretchr/testify 时,编写或审查测试、mock、测试套件的可执行做法。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
用 testify 写 Go 测试时如何通用
解决思路
- 始终以 *testing.T 作为入口,testify 只是补充而非替代标准 testing 包。
- 区分 assert 与 require:assert 记录失败后继续执行以便一次看到所有失败;require 调用 t.FailNow(),用于继续执行会 panic 或产生误导的前置条件。
- 用 assert.New(t) / require.New(t) 提升可读性,并分别命名为 is 和 must。
- 遵循规则:require 用于前置条件(setup、错误检查),assert 用于验证,不要随意混用。
- 断言参数顺序始终为 (expected, actual),交换顺序会产生令人困惑的 diff 输出。
- 使用核心断言:Equal/NotEqual(DeepEqual + 精确类型)、EqualValues(先转换为共同类型)、EqualExportedValues。
- 使用 Nil/NotNil、True/False、Empty/NotEmpty、Len 检查空值与长度。
- 使用 Contains 检查字符串、切片元素或 map 键。
- 使用 Greater/Less、Positive/Negative、Zero 做比较。
- 使用 Error/NoError、ErrorIs(遍历错误链)、ErrorAs、ErrorContains 检查错误。
- 使用 IsType、Implements 检查类型。
- 使用高级断言:ElementsMatch(无序比较)、InDelta(浮点容差)、JSONEq(忽略空白与键顺序)、WithinDuration、Regexp。
- 使用 Eventually 做异步轮询,或使用 EventuallyWithT 在轮询中执行丰富断言(通过 *assert.CollectT)。
- 使用 testify/mock 隔离被测单元:嵌入 mock.Mock,用 m.Called() 实现方法,并始终用 AssertExpectations(t) 验证。
- 使用 mock 匹配器 mock.Anything、mock.AnythingOfType("T")、mock.MatchedBy(func),以及调用修饰符 .Once()、.Times(n)、.Maybe()、.Run(func)。
- 使用 testify/suite 组织共享 setup/teardown 的测试,生命周期为 SetupSuite → SetupTest → TestXxx → TearDownTest → TearDownSuite。
- suite 中 s.Equal() 等行为类似 assert;需要 require 时用 s.Require().NotNil(obj)。
- 必须编写启动函数 func TestXxxSuite(t *testing.T) { suite.Run(t, new(XxxSuite)) },否则测试会静默不执行。
- 避免常见错误:忘记 AssertExpectations(t)、用 is.Equal 比较包装错误(应改用 is.ErrorIs)、参数顺序颠倒、用 assert 做守卫、缺少 suite.Run()、直接比较指针地址(应解引用或使用 EqualExportedValues)。
- 使用 testifylint 捕获参数# 顺序错误、assert/require 误用等问题。
适用边界 · 注意事项
- 代码库未导入 github.com/stretchr/testify 时。
- 需要替代标准 testing 包而非补充它时(testify 不替代 testing)。