---
id: daily-golang-stretchr-testify
name: "golang-stretchr-testify"
url: https://skills.yangsir.net/skill/daily-golang-stretchr-testify
author: samber
domain: testing
tags: ["unit-testing", "test-automation", "testing", "testimony", "tdd"]
install_count: 38900
rating: 4.60 (20 reviews)
github: https://github.com/samber/cc-skills-golang
---

# golang-stretchr-testify

> 基于 testify 框架编写可读性强的 Go 测试用例，使用断言和 mock 提升测试代码的可维护性

**Stats**: 38,900 installs · 4.6/5 (20 reviews)

## Before / After 对比

### 测试代码可读性对比

**Before**:

使用标准 testing 包编写测试，大量的 if/assert.NoError(err) 模板代码，测试逻辑被噪音淹没，新人需要 30 分钟才能理解一个测试用例的意图

**After**:

使用 testify 的断言链和 mock 功能，测试代码简洁明了，意图一目了然，添加新测试用例只需 5 分钟，测试失败时的错误信息更加友好

| Metric | Before | After | Change |
|---|---|---|---|
| 测试用例编写时间 | 30分钟 | 5分钟 | -83% |

## Readme

# golang-stretchr-testify

**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](https://github.com/samber/cc-skills-golang/blob/HEAD/skills/golang-stretchr-testify/./references/mock.md).

## 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. 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`

## Linters

Use `testifylint` to catch wrong argument order, assert/require misuse, and more. See `samber/cc-skills-golang@golang-linter` skill.

## 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

Weekly Installs718Repository[samber/cc-skills-golang](https://github.com/samber/cc-skills-golang)GitHub Stars1.1KFirst SeenMar 22, 2026Security Audits[Gen Agent Trust HubPass](/samber/cc-skills-golang/golang-stretchr-testify/security/agent-trust-hub)[SocketPass](/samber/cc-skills-golang/golang-stretchr-testify/security/socket)[SnykPass](/samber/cc-skills-golang/golang-stretchr-testify/security/snyk)Installed onopencode682cursor676codex673gemini-cli671github-copilot670amp669

---
*Source: https://skills.yangsir.net/skill/daily-golang-stretchr-testify*
*Markdown mirror: https://skills.yangsir.net/api/skill/daily-golang-stretchr-testify/markdown*