关于 golang-stretchr-testify
面向 Go 工程师的 stretchr/testify 测试技能,适用于编写或审查测试。内容侧重断言失败行为、Mock 验证、测试套件及常见误用。
主要功能
- 01assert 与 require 的使用指南
- 02基础及高级断言
- 03Mock 设置与预期验证
- 04测试套件生命周期与执行
- 05常见错误及 Linter 指引
适用场景
- 01为 Go 项目编写行为测试
- 02检查测试中的断言误用
- 03Mock 接口并验证预期调用
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ samber/ cc-skills-golang --skill golang-stretchr-testify安装后在 Claude Code 中输入/golang-stretchr-testify确认已加载。
附带文件evals/evals.jsonreferences/mock.md
详细介绍
角色: 你是一名 Go 工程师,将测试视为可执行规范。你编写测试来约束行为,并让失败原因一目了然,而不是为了达到覆盖率目标。
模式:
- 编写模式 — 向代码库添加新测试或 Mock。
- 审查模式 — 检查现有测试代码是否误用 testify。
stretchr/testify
testify 为 Go 的 testing 包提供可读的断言、Mock 和测试套件。它不会取代 testing —— 始终使用 *testing.T 作为入口。
本技能并非详尽无遗 —— 如需更多信息,请参阅库文档和代码示例:
- 有关 Go 包文档、符号、版本、导入方和已知漏洞,→ 请参阅
samber/cc-skills-golang@golang-pkg-go-dev技能(godig),查询 Go 包信息时优先于 Context7。 - 如需在你自己的代码中浏览此库的用法(定义、调用位置、诊断信息),→ 请参阅
samber/cc-skills-golang@golang-gopls技能(gopls)。 - 对于未被 pkg.go.dev 收录的文档,Context7 仍可作为备用选择。
assert 与 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) // stop if parsing fails — cfg would be 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)
// Equality
is.Equal(expected, actual) // DeepEqual + exact type
is.NotEqual(unexpected, actual)
is.EqualValues(expected, actual) // converts to common type first
is.EqualExportedValues(expected, actual)
// Nil / Bool / Emptiness
is.Nil(obj) is.NotNil(obj)
is.True(cond) is.False(cond)
is.Empty(collection) is.NotEmpty(collection)
is.Len(collection, n)
// Contains (strings, slices, map keys)
is.Contains("hello world", "world")
is.Contains([]int{1, 2, 3}, 2)
is.Contains(map[string]int{"a": 1}, "a")
// Comparison
is.Greater(actual, threshold) is.Less(actual, ceiling)
is.Positive(val) is.Negative(val)
is.Zero(val)
// Errors
is.Error(err) is.NoError(err)
is.ErrorIs(err, ErrNotFound) // walks error chain
is.ErrorAs(err, &target)
is.ErrorContains(err, "not found")
// Type
is.IsType(&User{}, obj)
is.Implements((*io.Reader)(nil), obj)
参数顺序:始终使用 (expected, actual) —— 交换顺序会产生令人困惑的差异输出。
高级断言
is.ElementsMatch([]string{"b", "a", "c"}, result) // unordered comparison
is.InDelta(3.14, computedPi, 0.01) // float tolerance
is.JSONEq(`{"name":"alice"}`, `{"name": "alice"}`) // ignores whitespace/key order
is.WithinDuration(expected, actual, 5*time.Second)
is.Regexp(`^user-[a-f0-9]+$`, userID)
// Async polling
is.Eventually(func() bool {
status, _ := client.GetJobStatus(jobID)
return status == "completed"
}, 5*time.Second, 100*time.Millisecond)
// Async polling with rich assertions
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() → once before all tests
SetupTest() → before each test
TestXxx()
TearDownTest() → after each test
TearDownSuite() → once after all tests
示例
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())
}
// Required launcher
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)。颠倒顺序会导致差异方向相反 - 用
assert检查守卫条件 —— 测试在失败后会继续执行,并因解引用 nil 而 Panic。应使用require - 缺少
suite.Run()—— 没有启动函数时,不会执行任何测试,而且不会提示 - 比较指针 ——
is.Equal(ptr1, ptr2)比较的是地址。请解引用,或使用EqualExportedValues
Linter
使用 testifylint 检测参数顺序错误、误用 assert/require 等问题。请参阅 samber/cc-skills-golang@golang-lint 技能。
交叉引用
- → 有关常见测试模式、表驱动测试和 CI,请参阅
samber/cc-skills-golang@golang-testing技能 - → 有关 testifylint 配置,请参阅
samber/cc-skills-golang@golang-lint技能