go-testing について
リポジトリ間で一貫した Go テストを書くためのスキルです。コーディングエージェント向けに、テスト構成、代替実装、データベーステスト、検証手順を示します。
主な機能
- 01公開 API 経由のブラックボックステスト
- 02状態を持つフェイクの優先
- 03実際の Postgres による統合テスト
- 04非同期・ファズテストの指針
- 05テストと lint のコマンド
活用シーン
- 01Go の機能にテストを追加
- 02SQL を使う動作のテスト
- 03並行 Go コードの確認
- 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.ymlassets/pgtest/pgtest_test.gopgtest.gosettings.gosweep_test.goevals/evals.jsonreferences/examples.mdintegration-db.md詳細
Go のテスト
すべてのリポジトリで、Go のテストをどのように書くかを説明します。何をテストするかは、リポジトリに testing-policy がある場合はその内容に従います。どの種類のテストを使うか、それぞれの価値、機能に必要なテスト、コードベースの成長に合わせて再利用できるパターンを扱います。テストの書き方を再度説明することはないため、両者が重複することはありません。レッド・グリーン・リファクタリングのサイクルについては、tdd を参照してください。
ここでは使わないデフォルトの習慣
つい無意識に行ってしまう習慣を挙げています。テストを書く前と、作業を終える前に、この表と照らし合わせてください。長い作業の終盤に、こうした習慣がまた出てくることがあります。
| つい使ってしまうもの | 代わりに使うもの |
|---|---|
testify の assert/require、その他のアサーションライブラリ | 通常の if と t.Errorf。スカラー値以外には cmp.Diff |
reflect.DeepEqual | cmp.Diff(want, got) を使い、(-want +got) として報告します |
wantErr bool を含むテーブルと if tt.wantErr | 1 つのテスト内に成功用と、errors.Is で確認するエラー用の 2 つのテーブルを作ります |
gomock、mockery、EXPECT().Times(1) | 状態を保持するインメモリのフェイクを使い、最終状態を検証します |
sqlmock またはモックした database/sql ドライバー | internal/pgtest の実際の Postgres を使います |
パッケージごとにコンテナを起動する TestMain | pgtest.New(t) を使います。リポジトリごとにコンテナを 1 つ、テストごとにデータベースを 1 つ使います |
テスト間の TRUNCATE、共有行 | テストごとに新しいデータベースを使い、t.Parallel() を安全に実行できるようにします |
ゴルーチンを待つための time.Sleep | コードが通知するチャネル、期限付きポーリング、または testing/synctest を使います |
strings.Contains(err.Error(), "...") | エクスポートされたセンチネルまたは型に対して errors.Is/errors.As を使います |
| 新しいテスト依存関係 | github.com/google/go-cmp のみを使います。それ以外は事前に相談してください |
ルール
- エクスポートされた API を通して振る舞いをテストし、ブラックボックスの
package foo_testを使います。ユニットが設計上エクスポートされていない場合に限りpackage fooを使い、export_test.goは最終手段とします。 - どの呼び出しが行われたかではなく、出力と状態を検証します。振る舞いを変えないリファクタリングで壊れるテストは変更検出器です。そのようなテストは削除してください。
- 最も忠実なテストダブルを選びます。実物、フェイク、スタブ、モックの順です。システム境界をまたぐ状態変更を伴う呼び出し(メール送信、カード決済など)に限ってモックを使い、その場合もモックライブラリではなく、手書きのレコーダーを使います。
- フェイクは状態を持ち、誠実に振る舞うものです。同じセンチネル、同じ Not Found の振る舞いを使います。1 つのコントラクトスイートをフェイクと実際の実装の両方で実行します。エラーパスを強制するには、フェイクに失敗用の設定項目を設けるのではなく、インターフェースを埋め込んだ 1 メソッドのスタブを使います。
- SQL は
internal/pgtestを通して実際の Postgres に対してテストします。リポジトリにinternal/pgtestがない場合は、このスキルのアセットからインストールします。references/integration-db.mdを読み込んでください。 cmp.Diff(want, got)で比較し、(-want +got)を報告します。go.modにgithub.com/google/go-cmpがない場合、最初に手を加えるテストでgo getを使って追加し、これに反するリポジトリ内の lint ルールやドキュメントも同じ変更で修正します。ユーザーまたはリポジトリが新しい依存関係を明示的に禁止している場合(「標準ライブラリのみ」など)は、スカラー値を==で、スライスとマップをslices.Equal/maps.Equalで、構造体をフィールドごとに比較します。いずれの場合もreflect.DeepEqualは使いません。- 失敗メッセージは
Func(in) = got, want wantの形式にします。エラーの判定にはerrors.Is/errors.Asを使います。呼び出し元がエラーを区別する必要がある場合は、エクスポートされたセンチネルまたは型を用意します。 - テスト対象の関数またはメソッドごとに、外側のテストを 1 つだけ用意します(
TestFunc、TestType_Method)。各ケースはすべてt.Runで実行します。ケースを同じ方法で検証できる場合は、nameフィールドを持つ構造体のスライスを使ったテーブルを作り、t.Run(tt.name, ...)で実行します。同じ方法で検証できない場合は、同じ外側のテスト内に名前付きサブテストを記述します。ケースのフィールドで分岐する場合は、1 つのテーブルではなく名前付きサブテストを使います。シナリオ名を付けた 2 つ目のトップレベルTestは作りません。 - 続行できる場合は
t.Error、残りの処理を実行できない場合はt.Fatalを使います。テストが起動していないゴルーチンからt.Fatalを呼び出してはいけません。エラーはチャネル経由で返します。 - ヘルパーは
tを受け取り、t.Helper()を呼び、セットアップエラーで失敗させ、t.Cleanupを登録します。ヘルパーはセットアップを行い、アサーションは行いません。汎用のassertEqualは作りません。 - 待機のために
time.Sleepを使ってはいけません。チャネルを待つか、期限付きで条件をポーリングするか、タイマーを使うテストではsynctest.Test(Go 1.25 以降)を実行します。 t.Parallel()を使えるのは、状態を共有しないテストだけです。グローバル変数、t.Setenv、t.Chdir、共有行がある場合は使いません。pgtestのデータベースは共有されないため、DB テストは並列実行できます。- JSON はバイト列としてではなく、デコードして比較します。比較の前にゴールデン出力を正規化し、すべての
-update差分を人が確認します。 - 信頼できない入力を解析するパーサーは、プロパティ(パニックしない、ラウンドトリップできるなど)を使ってファジングします。
testdata/fuzz/をコミットします。 - 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 | テストすべき対象:テストの種類、その価値、再利用可能なパターン(リポジトリごと) |