testing/synctest: add Subtest function
要約
概要
testing/synctest パッケージに Subtest 関数を追加する提案です。これは testing.T.Run と synctest.Test を組み合わせたショートカット関数であり、テーブル駆動テスト(table-driven test)でのコードのネスト(字下げ)を1段階削減することを目的としています。
ステータス変更
(なし) → active
2026年5月27日に開催されたProposal Review Meeting(@adonovan, @bradfitz, @cherrymui, @griesemer, @ianlancetaylor, @neild, @rolandshoemaker が参加)において、このプロポーザルが「active」カラムに追加され、週次レビューの対象となりました。@griesemer は「議論の余地は少ないが、必要性が明確かどうかは不明」とコメントしており、引き続き議論が進められている段階です。
技術的背景
現状の問題点
testing/synctest パッケージの Test 関数はバブル(bubble)と呼ばれる独立した時間制御環境を作成しますが、サブテストを書く際に t.Run と synctest.Test を組み合わせると、コードのネストが深くなるという問題があります。特にテーブル駆動テストでは、ループ・t.Run・synctest.Test の3段階のネストが生じます。
この問題は testing/synctest の導入以来多くの開発者が体験しており、x/net/http2 や x/net/internal/http3 など複数のパッケージで独自のヘルパー関数が実装されています。
提案された解決策
以下のシグネチャを持つ synctest.Subtest 関数を追加します。
func Subtest(t *testing.T, name string, f func(*testing.T)) {
t.Helper()
t.Run(name, func(t *testing.T) {
t.Helper()
Test(t, f)
})
}
この関数は t.Run の内部で synctest.Test を呼ぶことと完全に等価であり、魔法的な動作は一切ありません。
これによって何ができるようになるか
t.Run と synctest.Test の組み合わせを1行に簡略化でき、テーブル駆動テストにおける過度なネストを解消できます。コードの可読性が向上し、コピー&ペーストミスによる「別のテスト関数を呼んでしまう」バグのリスクも低減されます。
コード例
// Before: 従来の書き方(t.Run + synctest.Test の組み合わせ)
for _, test := range []struct {
name string
}{
{name: "test 1"},
} {
t.Run(test.name, func(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
// テスト本体(ネストが深い)
})
})
}
// After: synctest.Subtest を使った書き方
for _, test := range []struct {
name string
}{
{name: "test 1"},
} {
synctest.Subtest(t, test.name, func(t *testing.T) {
// テスト本体(ネストが1段階浅くなる)
})
}
議論のハイライト
- 提案者の @neild 自身が「trivial(些細)な関数である一方、だからこそ標準ライブラリに入れる価値がある」という矛盾した立場を認めており、類似の
synctest.Sleep(#77169、承認済み)の議論と同様の構図となっています。 - 関連 Issue #77169(
synctest.Sleep)のレビュー中に @nicholashusin がx/netの複数パッケージで同一のヘルパーが実装されている事実を指摘し、本提案の実用性を後押ししています。 - @griesemer は「controversial ではないが、必要かどうかは不明」と述べており、標準ライブラリへの追加基準(「誰もが書くような些細なコードを提供する価値があるか」)を慎重に見極めている状況です。
- 類似の
synctest.Sleepは最終的に「テスト内ではほぼ必ずこのパターンを使う」という実績から承認されており、Subtestも同様の判断がなされる可能性があります。 - 現在は「active」ステータスで議論継続中であり、まだ「likely accept」や「accepted」には至っていません。