メインコンテンツへスキップ

Go Proposal Weekly Digest

Go言語のproposal更新を毎週お届け

#64993active

testing: allow examples the use of testing.T/B/F for test framework examples

新規提案

要約

AIによる要約であり、誤りを含む場合があります。

概要

Example 関数が *testing.T*testing.B、または *testing.F を引数として受け取れるようにする提案です。これによりテストフレームワークやテストヘルパーライブラリの開発者が、testing.T を必要とするコードの実行可能な例をドキュメントに掲載できるようになります。

ステータス変更

(なし)active
2026年5月27日のProposal Review Meetingにおいて、@griesemer がコアチームを代表して提案の骨格を整理してコメントし、議論継続のために active ステータスへ移行しました。@neild が「testing パッケージ自体のドキュメントのためだけでも価値がある」と支持を表明したこと、および testing/synctest の新API(synctest.Test)が *testing.T を必要とするようになったことで、ユースケースが増えたことが後押しになったと考えられます。

技術的背景

現状の問題点

現在の Example 関数はゼロ引数・戻り値なしという形式に限定されており、実行可能な例として扱われます。しかし testing.TB インターフェース(またはその実装である *testing.T など)を必要とするAPIを持つライブラリ(テストフレームワーク、テストヘルパー、testing/synctest など)は、ドキュメント用の例を書く際に困難に直面します。

// 現状のワークアラウンド: ダミーの testing.T を作るか、
// Example 関数の中で synctest.Test に渡すための空の T を用意する必要がある
func ExampleTimeout() {
    // testing.T を使えないため、時刻を固定してダミー処理をする
    // 実際のコードの意図が伝わりにくい
}

また // Output: コメントによる出力検証は単純なテキストマッチングしかできないため、システム依存の出力・バイナリデータ・長い出力を扱う例では例のコード自体が複雑になり、本来の目的(シンプルさ)に反します。

提案された解決策

Example 関数が以下のいずれかの形式を取れるようにします。

  1. 従来どおり引数なし・戻り値なし
  2. *testing.T(および場合によって *testing.B)を引数として受け取る(戻り値なし)
  3. 引数なしで error を返す(関連提案 #21111 が承認された場合)
    *testing.T 引数を持つ Example 関数は、go test 実行時に通常の Test... 関数と同様に実行されます。pkg.go.dev などのドキュメントサイトでは、Example プレフィックスを Test プレフィックスに置き換えてコードを表示する形式が検討されています。
    // Output: コメントとの組み合わせについては、*testing.T を引数に持つ場合の // Output:os.Stdout への出力のみを対象とし、t.Log などの出力は含まないという方向で議論が進んでいます。

これによって何ができるようになるか

テストフレームワークやヘルパーライブラリの開発者が、*testing.T を活用した実行可能な例をドキュメントに直接掲載できるようになります。

コード例

// Before: 従来の書き方(testing.T が使えないため表現力が限られる)
func ExampleDiff() {
    got := MakeGatewayInfo()
    want := Gateway{ /* ... */ }
    diff := cmp.Diff(want, got)
    fmt.Println(diff)
    // Output:
    // ... (複雑な出力文字列)
}
// After: *testing.T を引数に持つ Example 関数
func ExampleDiff(t *testing.T) {
    got := MakeGatewayInfo()
    want := Gateway{ /* ... */ }
    if diff := cmp.Diff(want, got); diff != "" {
        t.Errorf("MakeGatewayInfo() mismatch (-want +got):\n%s", diff)
    }
}
// synctest を使った例も自然に書けるようになる
func ExampleTimeout(t *testing.T) {
    synctest.Test(t, func(t *testing.T) {
        ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
        defer cancel()
        // ...
    })
}

議論のハイライト

  • testing.B の有用性が未確定: @griesemer はベンチマークの書き方を示すためにも有用かもしれないと述べた一方、@dolmen*testing.B のユースケースはニッチすぎると指摘。Go Playground も *testing.B をサポートしていないため、まず実装して判断するという方針が取られた。
  • // Output: との組み合わせ問題: *testing.T を引数に持つ場合、t.Log の出力にはファイル名・行番号が含まれるため、// Output: コメントとの整合性が問題となる。@neild// Output:os.Stdout のみを対象とすべきと提案。@dolmen は両者を排他的とし、lint で検出すべきと提案した。
  • // Output: のない従来の Example との実行方針: 引数なし・戻り値なし・// Output: コメントなしの場合は従来どおり実行しない(パニックしないことの確認のみ)。それ以外の形式(引数あり、戻り値あり、または // Output: あり)は常に実行するという方針が @neild によって整理された。
  • 失敗テストの例示ニーズ: @apparentlymart は、テストユーティリティライブラリでは「失敗するテスト」を例示したいケースも多いと指摘し、// FAIL: コメントのような新しいアノテーション形式を提案した。
  • 実装CL 679655の存在: @dolmengo/doc パッケージへの変更を部分的に実装済み(CL 679655)。pkgsite 側の変更なしに Example...(t *testing.T) を表示できる実装が行われており、提案の実現可能性が示されている。

関連リンク