为 Go 代码产出可上生产的测试:表驱动用例、子测试命名、goleak 检漏、模糊测试与 CI 集成。
集成
golang-stretchr-testify
试用编写并审查使用 testify 的 Go 测试,合理使用断言、Mock 和测试套件。
它能做什么
提供 stretchr/testify 与 Go testing 包配合使用的指导,包括 assert 和 require 的行为差异、常用断言、Mock 期望、参数匹配器和 Suite 生命周期。内容覆盖前置条件与行为验证、包装错误检查、Eventually 异步轮询,以及参数顺序和断言误用等常见问题。文档也说明自身并非穷尽性资料,并建议结合库文档和代码示例了解更多信息。
什么时候用它
- 为 Go 测试选择 assert 或 require
- 为接口创建并验证 testify Mock
- 使用 suite 组织共享初始化和清理的测试
- 审查断言参数顺序、错误链和套件启动方式
技能文档
Persona: You are a Go engineer who treats tests as executable specifications. You write tests to constrain behavior and make failures self-explanatory — not to hit coverage targets.
Modes:
- Write mode — adding new tests or mocks to a codebase.
- Review mode — auditing existing test code for testify misuse.
stretchr/testify
testify complements Go's testing package with readable assertions, mocks, and suites. It does not replace testing — always use *testing.T as the entry point.
This skill is not exhaustive. Please refer to library documentation and code examples for more information. Context7 can help as a discoverability platform.
assert vs require
Both offer identical assertions. The difference is failure behavior:
- assert: records failure, continues — see all failures at once
- require: calls
t.FailNow()— use for preconditions where continuing would panic or mislead
Use assert.New(t) / require.New(t) for readability. Name them is and 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)
}
Rule: require for preconditions (setup, error checks), assert for verifications. Never mix randomly.
Core Assertions
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)
Argument order: always (expected, actual) — swapping produces confusing diff output.
Advanced Assertions
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 interfaces to isolate the unit under test. Embed mock.Mock, implement methods with m.Called(), always verify with AssertExpectations(t).
Key matchers: mock.Anything, mock.AnythingOfType("T"), mock.MatchedBy(func). Call modifiers: .Once(), .Times(n), .Maybe(), .Run(func).
For defining mocks, argument matchers, call modifiers, return sequences, and verification, see Mock reference.
testify/suite
Suites group related tests with shared setup/teardown.
Lifecycle
SetupSuite() → once before all tests
SetupTest() → before each test
TestXxx()
TearDownTest() → after each test
TearDownSuite() → once after all tests
Example
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))
}
Suite methods like s.Equal() behave like assert. For require: s.Require().NotNil(obj).
Common Mistakes
- Forgetting
AssertExpectations(t)— mock expectations silently pass without verification is.Equal(ErrNotFound, err)— fails on wrapped errors. Useis.ErrorIsto walk the chain- Swapped argument order — testify assumes
(expected, actual). Swapping produces backwards diffs assertfor guards — test continues after failure and panics on nil dereference. Userequire- Missing
suite.Run()— without the launcher function, zero tests execute silently - Comparing pointers —
is.Equal(ptr1, ptr2)compares addresses. Dereference or useEqualExportedValues
Linters
Use testifylint to catch wrong argument order, assert/require misuse, and more. See samber/cc-skills-golang@golang-lint skill.
Cross-References
- → See
samber/cc-skills-golang@golang-testingskill for general test patterns, table-driven tests, and CI - → See
samber/cc-skills-golang@golang-lintskill for testifylint configuration
常见问题
- 它会替代 Go testing 包吗?
- 不会。它补充 Go 的 testing 包,并以 *testing.T 作为测试入口。
- 什么时候应该使用 require?
- 用于前置条件、初始化步骤和错误检查;它会调用 t.FailNow(),而 assert 会记录失败后继续执行。
- 如何验证 Mock 期望?
- 嵌入 mock.Mock,使用 m.Called() 实现被 Mock 的方法,并调用 AssertExpectations(t)。如果缺少这一步,期望不会得到验证,测试也可能静默通过。
相关技能
为清晰、可测试、可组合而设计 Go 类型与接口。
写出无泄漏、可正确退出与传递错误的 Go 并发代码,并审阅 PR 与代码库。
提供 Go 命名规范细则,涵盖包名、类型、错误、布尔、接收者、常量与测试。
使用 Cobra + Viper 构建、扩展和审查 Go CLI:覆盖标志、配置分层、退出码、信号处理、Shell 补全与测试。
Provides resources to stay updated with Golang news, communities and people to follow. Use when seeking Go learning resources, discovering new libraries, finding community channels, or keeping up with Go language changes and releases.