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 時適用。

2261星標
150分支
更新於 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)

// Nil / 布林 / 空值
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,使用 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

Linter

使用 testifylint 來捕捉錯誤的參數順序、assert/require 誤用等。請參閱 samber/cc-skills-golang@golang-lint 技能。

交叉參考

  • → 請參閱 samber/cc-skills-golang@golang-testing 技能以了解一般測試模式、表格驅動測試和 CI
  • → 請參閱 samber/cc-skills-golang@golang-lint 技能以了解 testifylint 設定