
golang-stretchr-testify
热门stretchr/testify 的全面指南,涵盖 Golang 测试中的 assert、require、mock 和 suite 包。适用于使用 testify 编写测试、创建 mock、设置测试套件或选择 assert 与 require 的场景。涵盖 testify 断言、mock 期望、参数匹配器、调用验证、套件生命周期以及 Eventually、JSONEq 和自定义匹配器等高级模式。当代码库导入 github.com/stretchr/testify 时应用。
stretchr/testify 的全面指南,涵盖 Golang 测试中的 assert、require、mock 和 suite 包。适用于使用 testify 编写测试、创建 mock、设置测试套件或选择 assert 与 require 的场景。涵盖 testify 断言、mock 期望、参数匹配器、调用验证、套件生命周期以及 Eventually、JSONEq 和自定义匹配器等高级模式。当代码库导入 github.com/stretchr/testify 时应用。
角色: 你是一位将测试视为可执行规范的 Go 工程师。你编写测试是为了约束行为并使失败信息一目了然——而不是为了达到覆盖率目标。
模式:
- 编写模式——向代码库添加新测试或 mock。
- 审查模式——审计现有测试代码中 testify 的误用。
stretchr/testify
testify 通过可读的断言、mock 和套件补充了 Go 的 testing 包。它不替代 testing——始终使用 *testing.T 作为入口点。
本技能并非详尽无遗。请参考库文档和代码示例以获取更多信息。Context7 可作为发现平台提供帮助。对于 Go 包文档、版本、符号和已知漏洞,→ 参见 samber/cc-skills-golang@golang-pkg-go-dev 技能。
assert vs require
两者提供相同的断言。区别在于失败行为:
- assert:记录失败,继续执行——一次看到所有失败
- require:调用
t.FailNow()——用于前提条件,如果继续执行会导致 panic 或误导
使用 assert.New(t) / require.New(t) 以提高可读性。将它们命名为 is 和 must:
func TestParseConfig(t *testing.T) {
is := assert.New(t)
must := require.New(t)
cfg, err := ParseConfig("testdata/valid.yaml")
must.NoError(err) // 如果解析失败则停止——cfg 将为 nil
must.NotNil(cfg)
is.Equal("production", cfg.Environment)
is.Equal(8080, cfg.Port)
is.True(cfg.TLS.Enabled)
}
规则:require 用于前提条件(设置、错误检查),assert 用于验证。切勿随意混用。
核心断言
is := assert.New(t)
// 相等性
is.Equal(expected, actual) // DeepEqual + 精确类型
is.NotEqual(unexpected, actual)
is.EqualValues(expected, actual) // 先转换为公共类型
is.EqualExportedValues(expected, actual)
// 空值 / 布尔 / 空性
is.Nil(obj) is.NotNil(obj)
is.True(cond) is.False(cond)
is.Empty(collection) is.NotEmpty(collection)
is.Len(collection, n)
// 包含(字符串、切片、map 键)
is.Contains("hello world", "world")
is.Contains([]int{1, 2, 3}, 2)
is.Contains(map[string]int{"a": 1}, "a")
// 比较
is.Greater(actual, threshold) is.Less(actual, ceiling)
is.Positive(val) is.Negative(val)
is.Zero(val)
// 错误
is.Error(err) is.NoError(err)
is.ErrorIs(err, ErrNotFound) // 遍历错误链
is.ErrorAs(err, &target)
is.ErrorContains(err, "not found")
// 类型
is.IsType(&User{}, obj)
is.Implements((*io.Reader)(nil), obj)
参数顺序:始终为 (expected, actual)——交换会产生令人困惑的 diff 输出。
高级断言
is.ElementsMatch([]string{"b", "a", "c"}, result) // 无序比较
is.InDelta(3.14, computedPi, 0.01) // 浮点容差
is.JSONEq(`{"name":"alice"}`, `{"name": "alice"}`) // 忽略空白/键顺序
is.WithinDuration(expected, actual, 5*time.Second)
is.Regexp(`^user-[a-f0-9]+$`, userID)
// 异步轮询
is.Eventually(func() bool {
status, _ := client.GetJobStatus(jobID)
return status == "completed"
}, 5*time.Second, 100*time.Millisecond)
// 带丰富断言的异步轮询
is.EventuallyWithT(func(c *assert.CollectT) {
resp, err := client.GetOrder(orderID)
assert.NoError(c, err)
assert.Equal(c, "shipped", resp.Status)
}, 10*time.Second, 500*time.Millisecond)
testify/mock
Mock 接口以隔离被测单元。嵌入 mock.Mock,用 m.Called() 实现方法,始终用 AssertExpectations(t) 验证。
关键匹配器:mock.Anything、mock.AnythingOfType("T")、mock.MatchedBy(func)。调用修饰符:.Once()、.Times(n)、.Maybe()、.Run(func)。
关于定义 mock、参数匹配器、调用修饰符、返回值序列和验证,请参见 Mock 参考。
testify/suite
套件将相关测试分组,共享设置/拆卸。
生命周期
SetupSuite() → 在所有测试之前执行一次
SetupTest() → 在每个测试之前执行
TestXxx()
TearDownTest() → 在每个测试之后执行
TearDownSuite() → 在所有测试之后执行一次
示例
type TokenServiceSuite struct {
suite.Suite
store *MockTokenStore
service *TokenService
}
func (s *TokenServiceSuite) SetupTest() {
s.store = new(MockTokenStore)
s.service = NewTokenService(s.store)
}
func (s *TokenServiceSuite) TestGenerate_ReturnsValidToken() {
s.store.On("Save", mock.Anything, mock.Anything).Return(nil)
token, err := s.service.Generate("user-42")
s.NoError(err)
s.NotEmpty(token)
s.store.AssertExpectations(s.T())
}
// 必需的启动函数
func TestTokenServiceSuite(t *testing.T) {
suite.Run(t, new(TokenServiceSuite))
}
套件方法如 s.Equal() 的行为类似于 assert。对于 require:s.Require().NotNil(obj)。
常见错误
- 忘记
AssertExpectations(t)——mock 期望会静默通过而不验证 is.Equal(ErrNotFound, err)——在包装错误上失败。使用is.ErrorIs遍历链- 参数顺序颠倒——testify 假定
(expected, actual)。颠倒会产生反向 diff - 使用
assert进行守卫——测试在失败后继续执行,并在 nil 解引用时 panic。应使用require - 缺少
suite.Run()——没有启动函数,零个测试会静默执行 - 比较指针——
is.Equal(ptr1, ptr2)比较地址。解引用或使用EqualExportedValues
代码检查工具
使用 testifylint 捕获错误的参数顺序、assert/require 误用等。参见 samber/cc-skills-golang@golang-lint 技能。
交叉引用
- → 参见
samber/cc-skills-golang@golang-testing技能,了解通用测试模式、表格驱动测试和 CI - → 参见
samber/cc-skills-golang@golang-lint技能,了解 testifylint 配置





