mcpField
G
golang-stretchr-testify

golang-stretchr-testify

github.com/samberテスト・品質3.7k39.8k 回インストール5 日前に更新
リポジトリ

golang-stretchr-testify について

stretchr/testify を使ったテストの作成・レビューを支援する、Go エンジニア向けのスキルです。アサーションの失敗動作、モックの検証、テストスイート、よくある誤用を扱います。

主な機能

  1. 01assert と require の使い分け
  2. 02基本・高度なアサーション
  3. 03モック設定と期待値の検証
  4. 04テストスイートのライフサイクルと実行
  5. 05よくある誤りとリンターの指針

活用シーン

  1. 01Go プロジェクトで振る舞いを検証するテストの作成
  2. 02テスト内のアサーション誤用の確認
  3. 03インターフェースのモックと呼び出し検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/samber/cc-skills-golang --skill golang-stretchr-testify

インストール後、Claude Code で/golang-stretchr-testifyを入力して読み込まれたか確認します。

同梱ファイルevals/evals.jsonreferences/mock.md

詳細

ペルソナ: テストを実行可能な仕様として捉える Go エンジニアです。カバレッジ目標を達成するためではなく、動作を制約し、失敗の理由が自明になるようなテストを書きます。

モード:

  • 作成モード — コードベースに新しいテストやモックを追加します。
  • レビュー モード — 既存のテストコードで testify が誤用されていないか監査します。

stretchr/testify

testify は Go の testing パッケージを、読みやすいアサーション、モック、スイートで補完します。testing に取って代わるものではありません。エントリーポイントには常に *testing.T を使用してください。

このスキルは網羅的ではありません。詳しくはライブラリのドキュメントとコード例を参照してください。

  • Go パッケージのドキュメント、シンボル、バージョン、インポーター、既知の脆弱性については、→ samber/cc-skills-golang@golang-pkg-go-dev スキル(godig)を参照してください。Go パッケージの情報については、Context7 よりもこちらを推奨します。
  • 独自のコード内でこのライブラリの使い方(定義、呼び出し箇所、診断情報)を調べるには、→ samber/cc-skills-golang@golang-gopls スキル(gopls)を参照してください。
  • pkg.go.dev にインデックスされていないドキュメントには、引き続き Context7 を代替手段として利用できます。

assert と require

どちらも同じアサーションを提供します。違いは失敗時の動作です。

  • assert:失敗を記録して処理を続行します。すべての失敗を一度に確認できます。
  • require:t.FailNow() を呼び出します。続行するとパニックが発生したり、誤解を招いたりするような前提条件に使用します。

読みやすさのために assert.New(t) / require.New(t) を使用します。名前は is と 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)
}

ルール:前提条件(セットアップ、エラーチェック)には require、検証には assert を使用します。場当たり的に混在させないでください。

基本的なアサーション

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) の順にします。入れ替えると、差分の出力が分かりにくくなります。

高度なアサーション

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.Mock を埋め込み、m.Called() を使用してメソッドを実装し、必ず AssertExpectations(t) で検証してください。

主なマッチャー:mock.Anything、mock.AnythingOfType("T")、mock.MatchedBy(func)。呼び出し修飾子:.Once()、.Times(n)、.Maybe()、.Run(func)。

モックの定義、引数マッチャー、呼び出し修飾子、戻り値のシーケンス、検証については、モックのリファレンスを参照してください。

testify/suite

スイートを使うと、関連するテストを共通のセットアップ/ティアダウンとともにまとめられます。

ライフサイクル
SetupSuite()    → once before all tests
  SetupTest()   → before each test
    TestXxx()
  TearDownTest() → after each test
TearDownSuite() → once after all tests
例
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))
}

s.Equal() のようなスイートメソッドは assert と同様に動作します。require を使うには、s.Require().NotNil(obj) のようにします。

よくある間違い

  • AssertExpectations(t) の呼び出し忘れ — 検証しないと、モックの期待値は何も通知されずに通過します
  • is.Equal(ErrNotFound, err) — ラップされたエラーでは失敗します。チェーンをたどるには is.ErrorIs を使用します
  • 引数の順序の入れ替え — testify は (expected, actual) を前提としています。入れ替えると差分が逆向きになります
  • ガード条件に assert を使用 — 失敗後もテストが続行され、nil の参照外しでパニックが発生します。require を使用します
  • suite.Run() の呼び出し忘れ — 起動関数がないと、テストが 1 件も実行されず、何も通知されません
  • ポインターの比較 — is.Equal(ptr1, ptr2) はアドレスを比較します。参照先を比較するか、EqualExportedValues を使用します

リンター

引数の順序の誤り、assert/require の誤用などを検出するには、testifylint を使用します。samber/cc-skills-golang@golang-lint スキルを参照してください。

関連項目

  • → 一般的なテストパターン、テーブル駆動テスト、CI については、samber/cc-skills-golang@golang-testing スキルを参照してください
  • → testifylint の設定については、samber/cc-skills-golang@golang-lint スキルを参照してください

コメント