過去に所属していたプロジェクトでは、CIが終わるまでに2.5時間ほどかかっていました。その中で極稀に対向システムのモックがうまく立ち上がっていなかったり、DBのセットアップが終わらないままテストを行ったりして、テストに失敗することがありました。
リトライすればだいたいは上手くいきますが、単純に2.5時間結果を待つ必要があります。しかも上手くいけばいいものの、「どうせリトライすればうまくいくだろう」と何も考えず再実行し、原因が別にあることに気づくこともありました。
今回の記事では、失敗したテストだけを自動リトライさせるライブラリ「test-retry-gradle-plugin」を試したことを記事にしています。
ちなみに、こういう不安定なテストをFlakyなテストと言います。Googleでも1.5%ほど発生していたようです。詳細は下記のブログで確認してください。
JaSST東京実行委員ブログ:【用語解説】John Micco氏の言う"Flaky"なテストとは? jasst-tokyo.hatenablog.jp
環境
- Java
- 11
- org.gradle.test-retry
- 1.1.9
- Gradle
導入方法
build.gradleに下記設定を追加する。テストクラス側には追加設定は不要です。
plugins { id 'org.gradle.test-retry' version '1.1.9' } test { useJUnitPlatform() retry { failOnPassedAfterRetry = false maxFailures = 5 maxRetries = 3 } }
各環境設定について
FailOnPassedAfterRetry
リトライが成功した時のテスト全体の挙動を成功扱いか、失敗扱いとするか決めます。
trueはretryすると必ず「BUILD FAILED」、falseはretryした上で成功すると「BUILD SUCCESS」となります。このライブラリを入れている時点で、false一択でしょうが、Flakyなテストを絶対洗い出したいという意思があればtrueでもいいと考えています。
デフォルトはfalse。
trueを設定した場合のログ
> There were failing tests. See the report at: file:///C:/Users/naina/products/cucumber-sample/build/reports/tests/test/index.html * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Get more help at https://help.gradle.org BUILD FAILED in 10s
falseを設定した場合のログ
There were failing tests. See the report at: file:///C:/Users/naina/products/cucumber-sample/build/reports/tests/test/index.html
BUILD SUCCESSFUL in 11s
IDEの見た目
NetBeans以外はリトライで「BUILD SUCCESS」になっても、見た目は失敗扱いになるようです。ちゃんと公式ページを読んでいなかったので、混乱しました。
他のIDEのEclipseやCIのJenkins等々の見た目に関しては、公式ページから確認してください。
maxFailures
リトライ前の失敗数が一定数を超えていた場合にリトライを行わない基準値を設定します。テスト100件失敗している時に、リトライを行っても別の原因がありそうですからね。
リトライ前の失敗数なので、リトライした回数を加算するということはありません。この設定値を5とし、テスト失敗数が4だった場合には、例えリトライ数が100回だろうと100回リトライしてしまいます。2回リトライしたらテスト失敗数が8になるから止まる、ということはありません。
Flakyなテスト数にもよりますが、感覚値として5くらいが妥当と考えています。
デフォルトは0で、いくつ失敗してもリトライします。
maxRetries
最大リトライ回数。無駄に複数回リトライしてもしょうがないので、2または3が妥当だと考えています。位置情報を使用するようなテストであれば、もっとFlakyになると思うので、状況に応じて増やしておく方がいいでしょう。
デフォルトは0で、リトライを行いません。
備考
ParameterizedTestに失敗すると、たとえ部分的に成功しても全部成功するまで繰り返します。
なので、実行時間がかかるテストに関してはParameterizedTestにしないほうがいいかもしれません。E2EやITでParameterizedTestにしたことないので、わかりませんが。
このライブラリと設定を入れると、ローカルでのテストもリトライを行うようになります。個人的にCIで失敗するのが嫌なだけで、ローカルはFlakyなテストであることが分かっていれば、リモートリポジトリにPushしてもいいと考えています。
今回はローカルとCI用のタスクを分けて作成しませんでしたが、必要に応じて分けた方が開発効率も上がります。TDDでテストのレッドを確認したいだけなのに、リトライ複数回走ると体験としては最悪です。
ソースコード
テストコード github.com
build.gradle github.com
終わりに
ほとんどDevelopers.IO様の記事を真似て「やってみた」だけで終わらせる予定でしたが、実装して引っかかった点が多かったのでブログにしました。最終的には、公式ページに書かれていることをある程度日本語化したようなものなので、有用な記事になったと考えています。
不安定なテストのことをどこかのPodcastで聞いたときは、フラジャイルなテストという表現をしていましたが、おそらく一般的なテストはFlakyなテストという表現だと思いますので、正しい単語の意味を覚えられて満足です。
この記事がお役に立ちましたら、各種SNSでのシェアや、今後も情報発信しますのでフォローよろしくお願いします。
参考
Gradle Blog様:Introducing flaky test mitigation tools blog.gradle.org
Developers.IO様:不安定なテストを自動で再実行するGradleプラグインを試してみた dev.classmethod.jp
当ライブラリのソースコード github.com
MVNRepository mvnrepository.com