dotnet test をCI で実行していて、永遠に終わらないのを仕込んでしまったのですがどう対処するかです。
目次
TL;DR
dotnet test
自体に--timeout
引数はないdotnet test
としては Runsettings のRunConfiguration.TestSessionTimeout
プロパティでtimeout制約を提供している- テスト全体の所要時間で決め打ちしてもいいなら
timeout
コマンドを組み合わせることもできる - テストメソッドごとに
[Fact(Timeout = int)]
で提供も可能
特に、CircleCI を Performance Plan で実行していると、クレジットが目も当てられないことになります。
異常なテストを捕捉したい
捉えたい異常は、あるテストケースに誤って無限に実行されるバグを仕込んでしまった状況です。
今回の状況では、CircleCI の no_output_timeout
ではタイムアウトがなされずテストが何時間も実行されていました。
どんなテストプロジェクトなのか見通してみます。
- 1つのテストプロジェクトには多くのテストケースを書いている
- CIで普段からテストを実行しているので
dotnet test
ごとの所要時間は判明している - VSでも実行していることから、ほとんどのテストケースは1ms で終わっていることが個別に判明している
- ごくまれにPRにおいて永遠にテストケースが実行されるバグが混じり、
dotnet test
が終了しない - CI 上は実行中のため failed にならず気づけない
この状況下では、やりたいことはテストケース1つ一つを気にするのではなく、テスト自体が異常かどうかを判別したくなります。
打てる手は大きく2つありそうです。
- テスト実行
dotnet test
をタイムアウトさせる - テストケースごとにタイムアウトを設定する
順にみてみましょう。
テスト実行 dotnet test
をタイムアウトさせる
実行環境は、 CircleCI です。
dotnet test
をタイムアウトさせるということは、個別のテストケースは気にせず、テストがこれぐらいの時間で終わってほしいことを明示することになります。
テストケースが数多くあり、どこで無限に終わらないバグを作るか予測はムズカシイので、個別のテストケースに Timeout をつけるよりも妥当そうです。
dotnet test
をRunsettings.TestSessionTimeout で実行タイムアウトさせる
dotnet test --timeout
のようなパラメーターがアレば簡単に設定できそうですが、残念ながら--timeout
パラメーターは用意されていません。
dotnet test
的には、RunSettings の RunConfiguration.TestSessionTimeout
でタイムアウトを設定できます。
RunSettings
は、2つの方法で設定できます。
RunSettings を dotnet test
の引数で実行時上書きする
dotnet test
の引数には、RunSettings arguments
を使った RunSettings の実行時上書きが提供されています。
RunSettings の指定は、[name]=[value]
ペアを --
引数の後ろで指定することで利用できます。
TestSessionTimeout はRunConfiguration の子要素で、ミリ秒で指定します。 例えば、テスト実行を1秒でタイムアウトするように指定するにはこう書きます。
dotnet test -- RunConfiguration.TestSessionTimeout=1000
このパラメーターは、テスト実行時間自体を見ているのでテスト開始前の時間は無視されます、純粋にテスト実行時間で指定すればいいので最高です。1
RunSettings をファイルで指定する
dotnet test
の-s
や --settings
引数を利用すると、RunSettings を定義した .runsettings
ファイルを指定できます。
例えば先ほどの Sample.runsettings
を指定するなら次のように書きます。
dotnet test --settings Sample.runsettings
ただ、.runsettingsファイルを指定するにはテストプロジェクトごとに runsettings ファイルのパスを仕込む必要があります。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <RunSettingsFilePath>$(SolutionDir)\example.runsettings</RunSettingsFilePath> </PropertyGroup> ... </Project>
Configure unit tests with a .runsettings file - Visual Studio | Microsoft Docs
実行時の引数指定のほうがやりやすいので、私はこの方法は避けています。
timeout コマンドで dotnet test
をタイムアウトさせる
.NETCore を想定しているので、CI 環境は Linux であることが多いかと思います。
ということは、安定のtimeout コマンドをつかってdotnet test
全体の時間に制約をかけることもできます。
timeout -sKill SECOND dotnet test
dotnet test
自体の実行時間が定まっているのであればなかなか便利なのでこれもおすすめです。
テストケースごとにタイムアウトを設定する
xUnit であれば、[Fact(Timeout = int)]
を使ってテストケースごとにタイムアウトを仕掛けることができます。
あらかじめ、このテストは時間がかかるなど個別にタイムアウトをさせたいケースでとても有用です。 特に、nightly バッチなどで実行させる時間のかかるテストでは個別に設定するのはうれしいでしょう。
実行側はタイムアウトを知らなくていいので、並列実行でも制御しやすいですしね。
まとめ
dotnet test
で無限テスト実行、やらないとは言い切れないのでそんな時はtimeout を設定してあげましょう。
REF
-
VsTest での接続タイムアウトは
VSTEST_CONNECTION_TIMEOUT
環境変数で調整できます。(デフォ90sec)↩