golang-stretchr-testify について
stretchr/testify を使ったテストの作成・レビューを支援する、Go エンジニア向けのスキルです。アサーションの失敗動作、モックの検証、テストスイート、よくある誤用を扱います。
主な機能
- 01assert と require の使い分け
- 02基本・高度なアサーション
- 03モック設定と期待値の検証
- 04テストスイートのライフサイクルと実行
- 05よくある誤りとリンターの指針
活用シーン
- 01Go プロジェクトで振る舞いを検証するテストの作成
- 02テスト内のアサーション誤用の確認
- 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スキルを参照してください