golang-stretchr-testify

golang-stretchr-testify

热门

stretchr/testify 的全面指南,涵盖 Golang 测试中的 assert、require、mock 和 suite 包。适用于使用 testify 编写测试、创建 mock、设置测试套件或选择 assert 与 require 的场景。涵盖 testify 断言、mock 期望、参数匹配器、调用验证、套件生命周期以及 Eventually、JSONEq 和自定义匹配器等高级模式。当代码库导入 github.com/stretchr/testify 时应用。

2261Star
150Fork
更新于 2026/6/6
SKILL.md
只读
名称
golang-stretchr-testify
描述

stretchr/testify 的全面指南,涵盖 Golang 测试中的 assert、require、mock 和 suite 包。适用于使用 testify 编写测试、创建 mock、设置测试套件或选择 assert 与 require 的场景。涵盖 testify 断言、mock 期望、参数匹配器、调用验证、套件生命周期以及 Eventually、JSONEq 和自定义匹配器等高级模式。当代码库导入 github.com/stretchr/testify 时应用。

角色: 你是一位将测试视为可执行规范的 Go 工程师。你编写测试是为了约束行为并使失败信息一目了然——而不是为了达到覆盖率目标。

模式:

  • 编写模式——向代码库添加新测试或 mock。
  • 审查模式——审计现有测试代码中 testify 的误用。

stretchr/testify

testify 通过可读的断言、mock 和套件补充了 Go 的 testing 包。它不替代 testing——始终使用 *testing.T 作为入口点。

本技能并非详尽无遗。请参考库文档和代码示例以获取更多信息。Context7 可作为发现平台提供帮助。对于 Go 包文档、版本、符号和已知漏洞,→ 参见 samber/cc-skills-golang@golang-pkg-go-dev 技能。

assert vs require

两者提供相同的断言。区别在于失败行为:

  • assert:记录失败,继续执行——一次看到所有失败
  • require:调用 t.FailNow()——用于前提条件,如果继续执行会导致 panic 或误导

使用 assert.New(t) / require.New(t) 以提高可读性。将它们命名为 ismust

func TestParseConfig(t *testing.T) {
    is := assert.New(t)
    must := require.New(t)

    cfg, err := ParseConfig("testdata/valid.yaml")
    must.NoError(err)    // 如果解析失败则停止——cfg 将为 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)

// 相等性
is.Equal(expected, actual)              // DeepEqual + 精确类型
is.NotEqual(unexpected, actual)
is.EqualValues(expected, actual)        // 先转换为公共类型
is.EqualExportedValues(expected, actual)

// 空值 / 布尔 / 空性
is.Nil(obj)                  is.NotNil(obj)
is.True(cond)                is.False(cond)
is.Empty(collection)         is.NotEmpty(collection)
is.Len(collection, n)

// 包含(字符串、切片、map 键)
is.Contains("hello world", "world")
is.Contains([]int{1, 2, 3}, 2)
is.Contains(map[string]int{"a": 1}, "a")

// 比较
is.Greater(actual, threshold)     is.Less(actual, ceiling)
is.Positive(val)                  is.Negative(val)
is.Zero(val)

// 错误
is.Error(err)                     is.NoError(err)
is.ErrorIs(err, ErrNotFound)      // 遍历错误链
is.ErrorAs(err, &target)
is.ErrorContains(err, "not found")

// 类型
is.IsType(&User{}, obj)
is.Implements((*io.Reader)(nil), obj)

参数顺序:始终为 (expected, actual)——交换会产生令人困惑的 diff 输出。

高级断言

is.ElementsMatch([]string{"b", "a", "c"}, result)             // 无序比较
is.InDelta(3.14, computedPi, 0.01)                            // 浮点容差
is.JSONEq(`{"name":"alice"}`, `{"name": "alice"}`)             // 忽略空白/键顺序
is.WithinDuration(expected, actual, 5*time.Second)
is.Regexp(`^user-[a-f0-9]+$`, userID)

// 异步轮询
is.Eventually(func() bool {
    status, _ := client.GetJobStatus(jobID)
    return status == "completed"
}, 5*time.Second, 100*time.Millisecond)

// 带丰富断言的异步轮询
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.Anythingmock.AnythingOfType("T")mock.MatchedBy(func)。调用修饰符:.Once().Times(n).Maybe().Run(func)

关于定义 mock、参数匹配器、调用修饰符、返回值序列和验证,请参见 Mock 参考

testify/suite

套件将相关测试分组,共享设置/拆卸。

生命周期

SetupSuite()    → 在所有测试之前执行一次
  SetupTest()   → 在每个测试之前执行
    TestXxx()
  TearDownTest() → 在每个测试之后执行
TearDownSuite() → 在所有测试之后执行一次

示例

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())
}

// 必需的启动函数
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)。颠倒会产生反向 diff
  • 使用 assert 进行守卫——测试在失败后继续执行,并在 nil 解引用时 panic。应使用 require
  • 缺少 suite.Run()——没有启动函数,零个测试会静默执行
  • 比较指针——is.Equal(ptr1, ptr2) 比较地址。解引用或使用 EqualExportedValues

代码检查工具

使用 testifylint 捕获错误的参数顺序、assert/require 误用等。参见 samber/cc-skills-golang@golang-lint 技能。

交叉引用

  • → 参见 samber/cc-skills-golang@golang-testing 技能,了解通用测试模式、表格驱动测试和 CI
  • → 参见 samber/cc-skills-golang@golang-lint 技能,了解 testifylint 配置