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

Go Proposal Weekly Digest

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

#79808active

testing: allow examples with any signature

新規提案

要約

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

概要

testing パッケージのサンプル関数(Example関数)に任意のシグネチャを許可するproposalです。現在は引数なし・戻り値なしの関数しかExample関数として認識されませんが、この変更により *testing.T を引数に取ったり、error を返すような関数もドキュメント上のExampleとして表示できるようになります。

ステータス変更

(新規)active
2026年6月10日のProposal Review Meeting(@aclements, @adonovan, @cherrymui, @griesemer, @ianlancetaylor, @neild, @rolandshoemaker 参加)にて、本proposalが議事録に追加され「active」(週次レビュー対象)に移行しました。同会議では、関連する #64993(*testing.T を引数に取るExampleを許可する案)と #21111(Exampleが error を返せるようにする案)がともに「declined」(却下)された一方、本proposalはそれらを包括的に解決するより簡潔なアプローチとして議論が継続されることになりました。

技術的背景

現状の問題点

現在のGoでは、Example関数は引数なし・戻り値なしでなければなりません。これはいくつかの実用的なケースで問題になります。

// 現在:エラーを無視するか、log.Fatalに頼るしかない
func ExampleOpen() {
    f, err := os.Open("testfile")
    if err != nil {
        log.Fatal(err) // 実際のコードでは return err と書くはず
    }
    defer f.Close()
    examplepackage.F(f)
}
// テストフレームワークのExample(現在は書けない)
func ExampleMyFramework_Run(t *testing.T) { // コンパイルエラー
    myframework.Run(t, func() { ... })
}

特にテストユーティリティライブラリやエラーを返す関数のExampleを書く場合、実際のコードとかけ離れた不自然な書き方を強いられていました。

提案された解決策

go doc および go/doc パッケージが Example または ExampleXxxXxx が小文字で始まらない)という名前の関数を、シグネチャにかかわらずExampleとして認識・表示するようにします。

  • 引数や戻り値がある場合、go doc は関数全体をそのまま(非実行形式の)Exampleとして表示する
  • testing パッケージは引数または戻り値を持つExampleを自動実行しない
  • // Output: コメントが付いた引数あり/戻り値ありのExampleに対して go test はエラーを報告する
// 提案後:自然なGoコードとしてExampleを書ける
func Example_returning_an_error() error {
    f, err := os.Open("testfile")
    if err != nil {
        return err // 実際のコードそのまま
    }
    defer f.Close()
    examplepackage.F(f)
    return nil
}
func ExampleFunc_taking_a_t(t *testing.T) {
    if err := examplepackage.Func(t); err != nil {
        t.Fatal(err)
    }
}
func Example_in_and_out(a, b int) string {
    return examplepackage.AddReturningString(a, b)
}

これらはテストパッケージによって自動実行はされませんが、対応するTest関数を書くことでテスト可能です。

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

1. エラーを返す自然なExampleコードの記述
エラー処理を含む実際のコードに近い形でExampleを書けるようになり、ドキュメントとして実践的な価値が高まります。
2. テストフレームワークのExampleサポート
*testing.T を引数に取るテストユーティリティライブラリ(例: vmtest, synctest など)のExampleをgo docに表示できます。
3. 引数ありの「使い方のデモ」Example
HTTPトランスポートの設定など、コードの使用方法を示す(実行は不要な)Exampleを書けます。

コード例

// Before: エラーハンドリングが不自然
func ExampleOpen() {
    f, _ := os.Open("testfile") // エラーを無視
    defer f.Close()
    pkg.Process(f)
}
// After: 実際のコードと同じ書き方
func ExampleOpen() error {
    f, err := os.Open("testfile")
    if err != nil {
        return err
    }
    defer f.Close()
    pkg.Process(f)
    return nil
}
// テストしたい場合は別途ラッパーを書く
func TestExampleOpen(t *testing.T) {
    if err := ExampleOpen(); err != nil {
        t.Fatal(err)
    }
}

議論のハイライト

  • 関連する二つのproposalが同日に却下: #64993(*testing.T を引数に取るExampleのみ許可)と #21111(error を返すExampleのみ許可)は「合意に変更なし」として同日に却下された。本proposalはこれらを統合した汎用的なアプローチとして評価されている。
  • シンプルさが評価された: @aclements は「一般的にExampleを書くのに役立ち、シンプルで良い」とレビューコメントで述べ、activeステータスへの移行を主導した。
  • // Output: コメントとの互換性問題: シグネチャが変わるとテストハーネスが自動実行できなくなるため、標準出力の検証に使われる // Output: コメント機構が使えなくなる。testing.Benchmark に類似した testing.Test 関数の追加が代替案として @neild から提案されている。
  • 失敗を期待するExampleのテスト: t.Fatal を呼ぶExampleをどうテストするかについて議論があったが、サブプロセスでテストを実行する方法(testing/synctestrunTest パターン)が紹介され、エルゴノミクスは低いが実現可能との見解が示された。
  • 「使い方デモ」と「効果デモ」の二種類: @neild は現在のExampleが「コードの効果を示すもの(実行可能)」と「使い方を示すもの(実行不要)」の二種類に分類できると指摘し、本proposalは後者のサポートに特に有用だと述べた。

関連リンク