TIGの伊藤真彦です。
この記事はGo1.17連載の3記事目です。
Go1.17からのtestingにおける新機能
Go1.17のリリースノートにこのような記載があります。
testing
Added a new testing flag -shuffle which controls the execution order of tests and benchmarks.The new T.Setenv and B.Setenv methods support setting an environment variable for the
duration of the test or benchmark.テストとベンチマークの実行順序を制御する新しいテストフラグ-shuffleを追加しました。
新しいT.SetenvおよびB.Setenvメソッドは、テストまたはベンチマークの期間中の環境変数の設定をサポートします。
意外と根深い課題を解決できる嬉しい機能ですね、今回はこの新機能の紹介をすることにします。
shuffleオプションについて
Goでテストを行う時はいくつかのフラグを追加できます。-run
で実行するテストを絞り込んだり、-cover
でカバレッジを計測したりといった機能があります。
公式ドキュメントはこちらです。
それらオプション群にshuffleが追加されました。
shuffleオプションの使い方
ドキュメントに下記のように追記されています。
-shuffle off,on,N |
go test -shuffle=on
のように利用できます。go test -shuffle=123
のように整数値を指定することで、ランダムな値の生成などにおけるseed値を指定できます。
テストをシャッフルすると何が嬉しいのか
テストの実行順番をランダムにする機能は、Ruby On Rails
でのテストにおけるデファクトスタンダードであるrspec
など、他の言語、ライブラリでも実装されています。
順番をランダムにすることで、前に書いたテストの実行結果に依存する状態を検知し、回避できることが最大の目的です。
前に書いたテストの実行結果に依存する状態とは、下記のようなケースが該当します。
- 前のテストケースでグローバル変数が宣言、変更された前提で次のテストケースが書かれている
- 前のテストケースでデータベースに保存された内容を次のテストケースで利用している
これらの書き方は基本的にバッドプラクティスです。
何らかの事情でテストケースや実装に変更が加わった際に、一見無関係なテストが落ちて混乱を招くことになります。テストは各ケースの実行ごとにデータベースの内容を掃除するなど、クリーンな状態を保ちましょう。テストをランダム実行することで、上記のバッドプラクティスを早期に炙り出すことが可能になります。
当該機能を追加したissueにおいても、グローバル変数の状態が変わる事で、テストの実行順序が実行結果に影響が出る例が記載されています。
Those tests pass, everything looks fine, but they’re order dependent. Running them in another order will fail.
To prevent such hidden and hard to debug mistakes we need to make the order of test random for each test build.これらのテストは合格し、すべてが正常に見えますが、順序によって異なります。それらを別の順序で実行すると失敗します。
このような隠れたデバッグの難しい間違いを防ぐために、テストビルドごとにテストの順序をランダムにする必要があります。
Goに限った話ではないので、テストを書くときは気を付けていきたいですね。
T.Setenv、B.Setenvについて
テストコード、並びにベンチマーク中に環境変数をセットできるようになりました。osパッケージのSetenvとの違いは、テストが終了するとセットした内容が破棄され、環境変数が汚染されない事です。
元々の環境変数がセットされている場合は、きちんと元の値に戻ります、気軽に環境変数を変更できるようになりました。
t.Parallel
実行後に利用すると環境変数の寿命の扱いが破綻するため、エラーが発生する点だけ要注意です。
サンプルを探したところ、下記のようにGo本体のテストでも早速大活躍していました。
func TestImportVendor(t *testing.T) { |
私たちのチームでは、今まではテスト実行時にはMakefileで環境変数を一通り整備してからテストを実行する運用をしていました。また、必要な環境変数が存在しない場合はエラーで落ちるロジックが保険として書かれているのですが、複雑度が低いためテストできていない事を許容していました。
これらの課題をGo1.17に上げ事で簡単に解決できる希望が見えてきました。
まとめ
Go1.17ではテストの実装を改善する為、2点の新機能が追加されている。
- 新しいテストフラグ-shuffle
- 環境変数をモックするT.SetenvおよびB.Setenv
見落とさず活用していきたいなと感じたので、連載のトピックとして取り上げてみました。