golang-stretchr-testify

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
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.
角色定位:你是一名将测试视为可执行规范的Go工程师。你编写测试是为了约束行为并让失败原因一目了然——而非仅仅为了达到覆盖率指标。
模式
  • 编写模式——为代码库添加新测试或mock。
  • 评审模式——审核现有测试代码中testify的误用情况。

stretchr/testify

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 informations. Context7 can help as a discoverability platform.
testify是对Go标准库
testing
的补充,提供可读性更强的断言、mock和测试套件功能。它不会替代
testing
——始终以
*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
    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
:
go
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.
两者提供完全相同的断言方法,区别在于失败后的行为:
  • assert:记录失败,继续执行测试——可一次性查看所有失败点
  • require:调用
    t.FailNow()
    终止测试——适用于如果继续执行会导致panic或结果误导的前置条件场景
使用
assert.New(t)
/
require.New(t)
提升代码可读性,可将它们命名为
is
must
go
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
用于业务逻辑验证。切勿随意混用。

Core 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
(expected, actual)
— swapping produces confusing diff output.
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
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.
Mock接口来隔离待测试单元。嵌入
mock.Mock
,使用
m.Called()
实现方法,务必用
AssertExpectations(t)
进行验证。
关键匹配器:
mock.Anything
,
mock.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 tests
SetupSuite()    → once before all tests
  SetupTest()   → before each test
    TestXxx()
  TearDownTest() → after each test
TearDownSuite() → once after all tests

Example

示例

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
s.Equal()
behave like
assert
. For require:
s.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
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. Use
    is.ErrorIs
    to walk the chain
  • Swapped argument order — testify assumes
    (expected, actual)
    . Swapping produces backwards diffs
  • assert
    for guards
    — test continues after failure and panics on nil dereference. Use
    require
  • Missing
    suite.Run()
    — without the launcher function, zero tests execute silently
  • Comparing pointers
    is.Equal(ptr1, ptr2)
    compares addresses. Dereference or use
    EqualExportedValues
  • 忘记调用
    AssertExpectations(t)
    ——mock预期会在未验证的情况下静默通过
  • is.Equal(ErrNotFound, err)
    ——对于包装后的错误会验证失败。应使用
    is.ErrorIs
    遍历错误链
  • 参数顺序颠倒——testify默认
    (expected, actual)
    顺序。颠倒会产生反向的差异结果
  • assert
    做前置检查
    ——失败后测试继续执行,可能会因空指针引用导致panic。应使用
    require
  • 缺少
    suite.Run()
    ——如果没有启动函数,测试会静默地零执行
  • 直接比较指针——
    is.Equal(ptr1, ptr2)
    比较的是内存地址。应解引用后比较,或使用
    EqualExportedValues

Linters

代码检查工具

Use
testifylint
to catch wrong argument order, assert/require misuse, and more. See
samber/cc-skills-golang@golang-linter
skill.
使用
testifylint
来捕获参数顺序错误、assert/require误用等问题。可参考
samber/cc-skills-golang@golang-linter
技能文档。

Cross-References

交叉参考

  • → See
    samber/cc-skills-golang@golang-testing
    skill for general test patterns, table-driven tests, and CI
  • → See
    samber/cc-skills-golang@golang-linter
    skill for testifylint configuration
  • → 如需了解通用测试模式、表驱动测试和CI相关内容,请查看
    samber/cc-skills-golang@golang-testing
    技能文档
  • → 如需
    testifylint
    配置相关内容,请查看
    samber/cc-skills-golang@golang-linter
    技能文档