mcpField
G
go-testing

go-testing

github.com/o-marsters-1997テスト・品質062 回インストール3 日前に更新
リポジトリ

go-testing について

リポジトリ間で一貫した Go テストを書くためのスキルです。コーディングエージェント向けに、テスト構成、代替実装、データベーステスト、検証手順を示します。

主な機能

  1. 01公開 API 経由のブラックボックステスト
  2. 02状態を持つフェイクの優先
  3. 03実際の Postgres による統合テスト
  4. 04非同期・ファズテストの指針
  5. 05テストと lint のコマンド

活用シーン

  1. 01Go の機能にテストを追加
  2. 02SQL を使う動作のテスト
  3. 03並行 Go コードの確認
  4. 04Go テストスイートのレビュー

インストール

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

$npx skills add https://github.com/o-marsters-1997/my-claude-code --skill go-testing

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

assets/
golangci-depguard.yml
assets/pgtest/
pgtest_test.go
pgtest.go
settings.go
sweep_test.go
evals/
evals.json
references/
examples.md
integration-db.md

詳細

Go のテスト

すべてのリポジトリで、Go のテストをどのように書くかを説明します。何をテストするかは、リポジトリに testing-policy がある場合はその内容に従います。どの種類のテストを使うか、それぞれの価値、機能に必要なテスト、コードベースの成長に合わせて再利用できるパターンを扱います。テストの書き方を再度説明することはないため、両者が重複することはありません。レッド・グリーン・リファクタリングのサイクルについては、tdd を参照してください。

ここでは使わないデフォルトの習慣

つい無意識に行ってしまう習慣を挙げています。テストを書く前と、作業を終える前に、この表と照らし合わせてください。長い作業の終盤に、こうした習慣がまた出てくることがあります。

つい使ってしまうもの代わりに使うもの
testify の assert/require、その他のアサーションライブラリ通常の if と t.Errorf。スカラー値以外には cmp.Diff
reflect.DeepEqualcmp.Diff(want, got) を使い、(-want +got) として報告します
wantErr bool を含むテーブルと if tt.wantErr1 つのテスト内に成功用と、errors.Is で確認するエラー用の 2 つのテーブルを作ります
gomock、mockery、EXPECT().Times(1)状態を保持するインメモリのフェイクを使い、最終状態を検証します
sqlmock またはモックした database/sql ドライバーinternal/pgtest の実際の Postgres を使います
パッケージごとにコンテナを起動する TestMainpgtest.New(t) を使います。リポジトリごとにコンテナを 1 つ、テストごとにデータベースを 1 つ使います
テスト間の TRUNCATE、共有行テストごとに新しいデータベースを使い、t.Parallel() を安全に実行できるようにします
ゴルーチンを待つための time.Sleepコードが通知するチャネル、期限付きポーリング、または testing/synctest を使います
strings.Contains(err.Error(), "...")エクスポートされたセンチネルまたは型に対して errors.Is/errors.As を使います
新しいテスト依存関係github.com/google/go-cmp のみを使います。それ以外は事前に相談してください

ルール

  1. エクスポートされた API を通して振る舞いをテストし、ブラックボックスの package foo_test を使います。ユニットが設計上エクスポートされていない場合に限り package foo を使い、export_test.go は最終手段とします。
  2. どの呼び出しが行われたかではなく、出力と状態を検証します。振る舞いを変えないリファクタリングで壊れるテストは変更検出器です。そのようなテストは削除してください。
  3. 最も忠実なテストダブルを選びます。実物、フェイク、スタブ、モックの順です。システム境界をまたぐ状態変更を伴う呼び出し(メール送信、カード決済など)に限ってモックを使い、その場合もモックライブラリではなく、手書きのレコーダーを使います。
  4. フェイクは状態を持ち、誠実に振る舞うものです。同じセンチネル、同じ Not Found の振る舞いを使います。1 つのコントラクトスイートをフェイクと実際の実装の両方で実行します。エラーパスを強制するには、フェイクに失敗用の設定項目を設けるのではなく、インターフェースを埋め込んだ 1 メソッドのスタブを使います。
  5. SQL は internal/pgtest を通して実際の Postgres に対してテストします。リポジトリに internal/pgtest がない場合は、このスキルのアセットからインストールします。references/integration-db.md を読み込んでください。
  6. cmp.Diff(want, got) で比較し、(-want +got) を報告します。go.mod に github.com/google/go-cmp がない場合、最初に手を加えるテストで go get を使って追加し、これに反するリポジトリ内の lint ルールやドキュメントも同じ変更で修正します。ユーザーまたはリポジトリが新しい依存関係を明示的に禁止している場合(「標準ライブラリのみ」など)は、スカラー値を == で、スライスとマップを slices.Equal/maps.Equal で、構造体をフィールドごとに比較します。いずれの場合も reflect.DeepEqual は使いません。
  7. 失敗メッセージは Func(in) = got, want want の形式にします。エラーの判定には errors.Is/errors.As を使います。呼び出し元がエラーを区別する必要がある場合は、エクスポートされたセンチネルまたは型を用意します。
  8. テスト対象の関数またはメソッドごとに、外側のテストを 1 つだけ用意します(TestFunc、TestType_Method)。各ケースはすべて t.Run で実行します。ケースを同じ方法で検証できる場合は、name フィールドを持つ構造体のスライスを使ったテーブルを作り、t.Run(tt.name, ...) で実行します。同じ方法で検証できない場合は、同じ外側のテスト内に名前付きサブテストを記述します。ケースのフィールドで分岐する場合は、1 つのテーブルではなく名前付きサブテストを使います。シナリオ名を付けた 2 つ目のトップレベル Test は作りません。
  9. 続行できる場合は t.Error、残りの処理を実行できない場合は t.Fatal を使います。テストが起動していないゴルーチンから t.Fatal を呼び出してはいけません。エラーはチャネル経由で返します。
  10. ヘルパーは t を受け取り、t.Helper() を呼び、セットアップエラーで失敗させ、t.Cleanup を登録します。ヘルパーはセットアップを行い、アサーションは行いません。汎用の assertEqual は作りません。
  11. 待機のために time.Sleep を使ってはいけません。チャネルを待つか、期限付きで条件をポーリングするか、タイマーを使うテストでは synctest.Test(Go 1.25 以降)を実行します。
  12. t.Parallel() を使えるのは、状態を共有しないテストだけです。グローバル変数、t.Setenv、t.Chdir、共有行がある場合は使いません。pgtest のデータベースは共有されないため、DB テストは並列実行できます。
  13. JSON はバイト列としてではなく、デコードして比較します。比較の前にゴールデン出力を正規化し、すべての -update 差分を人が確認します。
  14. 信頼できない入力を解析するパーサーは、プロパティ(パニックしない、ラウンドトリップできるなど)を使ってファジングします。testdata/fuzz/ をコミットします。
  15. HTTP ハンドラーは、main と同じ方法で構築した実際のルーターとミドルウェアを通してテストします。

実行方法

go test ./... を実行すると、データベーステストを含むすべてのテストが実行されます。ビルドタグや -short によるスキップ、make ターゲットはありません。ビルドタグを使うのはエンドツーエンドのテストスイートだけです。Docker も TEST_DATABASE_URL もない場合、pgtest は両方の名前を示すメッセージを出して失敗します。t.Skip でごまかしてはいけません。完了前に go test -race ./... と golangci-lint run を実行します。

参照先

作成するもの…読み込むもの
Postgres にアクセスするテスト、または internal/pgtest がまだないリポジトリreferences/integration-db.md
データベーステストの CI(TEST_DATABASE_URL、サービスコンテナ)references/integration-db.md
単体テスト、テーブルテスト、フェイクとコントラクトテストの組み合わせ、HTTP、非同期、ゴールデン、ファズ、ベンチマークテストreferences/examples.md

assets/pgtest/ にはヘルパーとそのテストが含まれています。assets/golangci-depguard.yml には、 testcontainers のインポートを internal/pgtest 内に限定する lint ルールが含まれています。

関連項目

スキル使用する場面
go-idiomaticテスト対象のコード全般
tdd一度に 1 つの振る舞いを対象に、先にテストを書く
testing-policyテストすべき対象:テストの種類、その価値、再利用可能なパターン(リポジトリごと)

コメント