🔁

【Go 1.22】tparagenをForループ変数のスコープ変更に対応した

2024/02/10に公開

概要

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ループ変数のスコープが変更されました。

https://go.dev/blog/loopvar-preview

ループごとにスコープが制限されることにより、予期しない動作が生じなくなりました。そのため、Go1.22からはループ変数の再定義も必要なくなります。

おわりに

というわけで、Go1.22に対応したtparagen、使ってみてくださいね。

ツール詳細

https://qiita.com/sho-hata/items/d9aa0e16de30bb382648

事例

https://tech.kanmu.co.jp/entry/2023/06/02/172458

https://speakerdeck.com/shohata/go-conference-2023

脚注
  1. https://qiita.com/sho-hata/items/19356a3525fa47cfd2b1 ↩︎

Discussion