golang-stretchr-testify
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePersona: 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.
角色定位:你是一名将测试视为可执行规范的Go工程师。你编写测试是为了约束行为并让失败原因一目了然——而非仅仅为了达到覆盖率指标。
模式:
- 编写模式——为代码库添加新测试或mock。
- 评审模式——审核现有测试代码中testify的误用情况。
stretchr/testify
stretchr/testify
testify complements Go's package with readable assertions, mocks, and suites. It does not replace — always use as the entry point.
testingtesting*testing.TThis skill is not exhaustive. Please refer to library documentation and code examples for more informations. Context7 can help as a discoverability platform.
testify是对Go标准库的补充,提供可读性更强的断言、mock和测试套件功能。它不会替代——始终以作为入口点。
testingtesting*testing.T本指南并非详尽无遗。如需更多信息,请参考官方库文档和代码示例。Context7可作为探索相关资源的平台。
assert vs require
assert 与 require
Both offer identical assertions. The difference is failure behavior:
- assert: records failure, continues — see all failures at once
- require: calls — use for preconditions where continuing would panic or mislead
t.FailNow()
Use / for readability. Name them and :
assert.New(t)require.New(t)ismustgo
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: for preconditions (setup, error checks), for verifications. Never mix randomly.
requireassert两者提供完全相同的断言方法,区别在于失败后的行为:
- assert:记录失败,继续执行测试——可一次性查看所有失败点
- require:调用终止测试——适用于如果继续执行会导致panic或结果误导的前置条件场景
t.FailNow()
使用 / 提升代码可读性,可将它们命名为和:
assert.New(t)require.New(t)ismustgo
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)
}规则:用于前置条件(初始化、错误检查),用于业务逻辑验证。切勿随意混用。
requireassertCore Assertions
核心断言方法
go
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 — swapping produces confusing diff output.
(expected, actual)go
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)Advanced Assertions
高级断言方法
go
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)go
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
testify/mock
Mock interfaces to isolate the unit under test. Embed , implement methods with , always verify with .
mock.Mockm.Called()AssertExpectations(t)Key matchers: , , . Call modifiers: , , , .
mock.Anythingmock.AnythingOfType("T")mock.MatchedBy(func).Once().Times(n).Maybe().Run(func)For defining mocks, argument matchers, call modifiers, return sequences, and verification, see Mock reference.
Mock接口来隔离待测试单元。嵌入,使用实现方法,务必用进行验证。
mock.Mockm.Called()AssertExpectations(t)关键匹配器:, , 。调用修饰符:, , , 。
mock.Anythingmock.AnythingOfType("T")mock.MatchedBy(func).Once().Times(n).Maybe().Run(func)For defining mocks, argument matchers, call modifiers, return sequences, and verification, see Mock reference.
testify/suite
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 testsSetupSuite() → once before all tests
SetupTest() → before each test
TestXxx()
TearDownTest() → after each test
TearDownSuite() → once after all testsExample
示例
go
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 behave like . For require: .
s.Equal()asserts.Require().NotNil(obj)go
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 behave like . For require: .
s.Equal()asserts.Require().NotNil(obj)Common Mistakes
常见错误
- Forgetting — mock expectations silently pass without verification
AssertExpectations(t) - — fails on wrapped errors. Use
is.Equal(ErrNotFound, err)to walk the chainis.ErrorIs - Swapped argument order — testify assumes . Swapping produces backwards diffs
(expected, actual) - for guards — test continues after failure and panics on nil dereference. Use
assertrequire - Missing — without the launcher function, zero tests execute silently
suite.Run() - Comparing pointers — compares addresses. Dereference or use
is.Equal(ptr1, ptr2)EqualExportedValues
- 忘记调用——mock预期会在未验证的情况下静默通过
AssertExpectations(t) - ——对于包装后的错误会验证失败。应使用
is.Equal(ErrNotFound, err)遍历错误链is.ErrorIs - 参数顺序颠倒——testify默认顺序。颠倒会产生反向的差异结果
(expected, actual) - 用做前置检查——失败后测试继续执行,可能会因空指针引用导致panic。应使用
assertrequire - 缺少——如果没有启动函数,测试会静默地零执行
suite.Run() - 直接比较指针——比较的是内存地址。应解引用后比较,或使用
is.Equal(ptr1, ptr2)EqualExportedValues
Linters
代码检查工具
Use to catch wrong argument order, assert/require misuse, and more. See skill.
testifylintsamber/cc-skills-golang@golang-linter使用来捕获参数顺序错误、assert/require误用等问题。可参考技能文档。
testifylintsamber/cc-skills-golang@golang-linterCross-References
交叉参考
- → See skill for general test patterns, table-driven tests, and CI
samber/cc-skills-golang@golang-testing - → See skill for testifylint configuration
samber/cc-skills-golang@golang-linter
- → 如需了解通用测试模式、表驱动测试和CI相关内容,请查看技能文档
samber/cc-skills-golang@golang-testing - → 如需配置相关内容,请查看
testifylint技能文档samber/cc-skills-golang@golang-linter