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

Go Proposal Weekly Digest

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

#79808likely_accept

testing: allow examples with any signature

ステータス変更: active likely_accept

要約

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

概要

testing パッケージのexample関数に任意のシグネチャ(引数・戻り値)を許可する提案。現在は引数・戻り値なしの関数のみがexampleとして認識されるが、これを緩和して *testing.T を受け取る関数やerrorを返す関数もドキュメントに表示できるようにする。

ステータス変更

activelikely_accept
2026年6月24日のProposal Review Meetingにて、十分な議論を経てコンセンサスが形成されたと判断され、最終コメント期間(Final Comment Period)に移行した。@neild によるまとめコメントが提案の最終形として参照されており、@aclements がreview groupを代表してlikely acceptと宣言した。

技術的背景

現状の問題点

現在、go test はexample関数として認識される条件を厳しく制限している。関数名が Example または ExampleXxx(Xxxが大文字始まり)である必要があり、かつ引数・戻り値が一切ない func() シグネチャのみが有効なexampleとして扱われる。
この制約により、以下のような問題が生じている。

// 現在のexample: エラーをlog.Fatalで握りつぶすしかない
func ExampleCreateTemp() {
    f, err := os.CreateTemp("", "example")
    if err != nil {
        log.Fatal(err)  // 実際のコードではreturn errと書くべきところ
    }
    defer os.Remove(f.Name())
    // ...
}

実際のGoコードでは error を返すパターンが一般的であるにもかかわらず、exampleではそれが書けないため、読者に誤ったコーディングスタイルを示してしまう。また、*testing.T を引数に取るテストヘルパーライブラリのexampleを書くことがまったくできない状況となっている。

提案された解決策

go doc は名前が Example または ExampleXxx(Xxxが大文字始まり)である関数を、引数・戻り値の有無に関わらずexampleとして表示する。変更点は以下の通り。

  • go doc: パラメータ付きのexample関数も通常のexampleと同様にドキュメントに表示する。ただし関数本体全体を表示し、playgroundでの実行ボタンは表示しない
  • go test: パラメータ付きのexample関数は自動実行しない。実行したい場合は別途ラッパーテスト関数を書く
  • 制約: パラメータ付きのexample関数に // Output: コメントを書くと go test がエラーを出す(この制約は既存のOutput検査メカニズムとの整合性のため)

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

任意のシグネチャを持つexample関数をドキュメントに表示できるようになり、より実践的・正確なコード例を書けるようになる。

コード例

// Before: log.Fatalでエラーを握りつぶすしかない(実際のGoコードと乖離)
func ExampleCreateTemp() {
    f, err := os.CreateTemp("", "example")
    if err != nil {
        log.Fatal(err)
    }
    defer os.Remove(f.Name())
    // ...
}
// After: errorを返す自然なGoコードとして書ける
func ExampleCreateTemp() error {
    f, err := os.CreateTemp("", "example")
    if err != nil {
        return err
    }
    defer os.Remove(f.Name())
    // ...
    return nil
}
// After: *testing.Tを受け取るテストヘルパーライブラリのexampleも書ける
func ExampleMyHelper_taking_a_t(t *testing.T) {
    if err := mylib.MyHelper(t, "option"); err != nil {
        t.Fatal(err)
    }
}
// パラメータ付きexampleをテストで検証する場合(別途ラッパーが必要)
func TestExampleCreateTemp(t *testing.T) {
    if err := ExampleCreateTemp(); err != nil {
        t.Fatal(err)
    }
}

議論のハイライト

  • 本提案は #64993(*testing.T のみ許可)と #21111(errorのみ返せる)の代替として提案された。 どちらの先行提案よりも汎用的で、特定の型への特別扱いを避けられる点が評価された。
  • // Output: コメントとの非互換性。 パラメータ付きexampleでは // Output: による自動テストが使えなくなるという懸念が出た。これについて @ianlancetaylor は「// Output: コメントのある例のみを go test が実行する現行ルールを維持すれば問題なく、将来的に #21111 を採用する余地もある」と整理した。
  • playgroundでの実行不可問題。 パラメータ付きexampleはplaygoundで実行できなくなる。@ianlancetaylor は「exampleをコピーしてplaygroundで試すことは引き続き可能であり、Run ボタンがないことは大きな問題ではない」とコメントした。
  • 将来的な testing.Test 関数の追加を別提案として検討。 @neildtesting.Benchmark と同様に testing.Test(func(t *testing.T)) TestResult のような関数を導入すれば、*testing.T を扱うexampleをplaygroundで実行可能にしつつ // Output: コメントも使えると提案した。これは本提案とは独立した別提案として議論を続ける方針となった。
  • 将来の #21111 採用との互換性確保。 本提案ではパラメータ付きexampleに // Output: を禁止しているため、将来 #21111 を採用しても既存のexampleに予期しないテスト実行が起きる心配はないと確認された。

関連リンク