🔁
【Go 1.22】tparagenをForループ変数のスコープ変更に対応した
概要
Goのテストコードを一気に並行動作できるようにするツール「tparagen」が、Go 1.22に対応しました。
tparagenは、下記のように並行化していないテストコードがあった場合、並行化コードを自動で埋め込むツールです。
実行前
func SampleTest(t *testing.T) {
testCases := []struct {
name string
}{{name: "foo"}}
for _, tc := range testCases {
t.Run(tc.name, func(x *testing.T) {
// do anything...
})
}
}
実行後
go < 1.22
今までは、実行後forループ変数(tc
)の再定義もするようにしていました。
func SampleTest(t *testing.T) {
+ t.Parallel()
testCases := []struct {
name string
}{{name: "foo"}}
for _, tc := range testCases {
+ tc := tc
t.Run(tc.name, func(x *testing.T) {
+ x.Parallel()
// do anything...
})
}
}
go >= 1.22
オプションを付与(--min-go-version=1.22
)すると、forループ変数を再定義しないようにしました。
func SampleTest(t *testing.T) {
+ t.Parallel()
testCases := []struct {
name string
}{{name: "foo"}}
for _, tc := range testCases {
t.Run(tc.name, func(x *testing.T) {
+ x.Parallel()
// do anything...
})
}
}
背景
今まではForループ変数への参照が、その反復の終了後も保持してしまう状態になっていました。
この仕様により、テーブル駆動テストによる並行動作時に一部のテストケースしか評価されないという問題[1]が発生していました。
そのため、今まではおまじないのようにForループ変数を再定義する必要がありました。tparagenでは自動で挿入するようになっています。
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(x *testing.T) {
...
ですが、Go 1.22より、Forループ変数のスコープが変更されました。
ループごとにスコープが制限されることにより、予期しない動作が生じなくなりました。そのため、Go1.22からはループ変数の再定義も必要なくなります。
おわりに
というわけで、Go1.22に対応したtparagen、使ってみてくださいね。
ツール詳細
事例
Discussion