😵‍💫

通れば嬉し 落ちれば嘆く Flaky Tests の 原因と対策

に公開

不安定なテスト

自動テストを行なっていると、テストコードもテスト対象も直していないのにテストが合格したり、しなかったりするテストに遭遇することが多いでしょう。
このような非決定的で不安定なテストが与える問題点については Martin Fowler が以下のように指摘しています

  1. 不安定なテストは役に立たないこと
  2. 非決定的なテストは、テストスイートの価値を完全に破壊しうること

テストが失敗したときのアラートが信頼性に欠けると、そのアラート自体が無視されるようになり、また、100個のテストスイートに10個の不安定なテストが含まれている場合、残りの健全なテストの結果も無視されるようになると述べています。

非決定的で不安定なテストは古くはErratic Tests[1]とも呼ばれていましたが、最近はFlaky Testsと呼ばれることが多くなっています[2]

本記事では、これらの不安定なテストを Flaky Tests と呼び、その原因と対策を論じた文献を紹介します。

不安定さの原因

この章ではFlaky Testsの原因にどういうものがあるか、そしてそれがどう分類されているかを確認します。
まず、古典的なErratic Testsの原因分類を紹介した後、いくつかの研究で分類されたFlaky Testsの原因についてまとめたものを確認します。

古典的な原因の分類

xUnit Patterns.com-Erratic Testでは不安定なテストの原因を以下のように分類しています。

原因 解説
Interacting Tests テストが何らかの形で他のテストに依存することが原因。独立したテストで動かなくなる場合や、別のテストで同じリソースを作成しようとしている場合などがある
Resource Leakage テストコードまたはテスト対象のシステムのメモリやCPUなどを使い尽くすことが原因
Resource Optimism DBなどの外部リソースが常に利用可能という楽観的前提が原因
Unrepeatable Test 初回実行と2回目以降でふるまいが異なることが原因。実質的に、テスト実行をまたいで自分自身と相互作用している
Test Run War 同時実行時の衝突。複数人が同時にテストを実行していると発生することが原因
Nondeterministic Test 単一のテスト実行でもランダムで非決定的なテストが原因。たとえば乱数の使用

xUnit Patternsでは不安定なテストについての原因切り分けを以下のように行うように助言をしています。

不安定なテストのトラブルシュートフロー
xUnit Test Patterns: Refactoring Test Code Figure 16.1. Troubleshooting an Erratic Testを和訳したもの

Figure 16.1. Troubleshooting an Erratic Testを和訳

これらの原因分類は初出は2007年になりますが、これは後述のFlaky Testsの研究の分類と近しいものになります。上記の図は2022年のEffective Software Testingの10.1.4 Tests should be repeatable and not flakyでも、問題のあるテストを見つけて修正する一助になるフローとして紹介されています。

Flaky Testsの原因とその割合

Flaky Testsの原因の分類については以下の研究があります。

  • (1) Q. Luo, F. Hariri, L. Eloussi, and D. Marinov. 2014. An Empirical Analysis of Flaky Tests.
    • コミット履歴から不安定さの原因を10個のカテゴリーに分類した
  • (2) A. Vahabzadeh, A. A. Fard, and A. Mesbah. 2015. An Empirical Study of Bugs in Test Code
    • バグレポートからテストバグの根本原因を5個のカテゴリーに分類した
  • (3) M. Eck, F. Palomba, M. Castelluccio, and A. Bacchelli. 2019. Understanding Flaky Tests: The Developer’s Perspective.
    • 開発者に自身が修正したFlaky Testsの原因を分類させて、14個のカテゴリーに分類した
  • (4) W. Lam, K. Muşlu, H. Sajnani, and S. Thummalapenta. 2020. A Study on the Lifecycle of Flaky Tests.
    • Microsoft 社内の六つの対象プロジェクトを対象にプルリクエストをもとに原因の分類を行った。同一順序でテスト実行しているため、Test Order Dependency が原因となるものは大幅に削減されている
  • (5) S. Gruber, S. Lukasczyk, F. Kroiß, and G. Fraser. 2021. An Empirical Study of Flaky Tests in Python.
    • 実際にPythonのテストを実行して、その実行トレースをもとに原因を分類した

Owainらはこれらの研究の結果をまとめてFlakyテストの原因を以下のようにまとめました。[3]

  • Intra-test(テストに完全に内在する不安定要因)
    • Concurrency
      • 複数スレッドが安全でない、または想定されていない方法で相互作用するテスト
      • Source: (1), (2), (3), (4), (5)
    • Randomness
      • 乱数データ生成器の結果を用いるテスト
      • Source: (1), (3), (4), (5)
    • Floating Point
      • 浮動小数点演算の結果を用いるテスト。
      • Source: (1), (3), (4)
    • Unordered Collection
      • 未順序コレクション型オブジェクトの特定の反復順序を仮定するテスト
      • Source: (1), (4), (5)
    • Too Restrictive Range
      • 妥当な出力範囲の一部がアサーションで受け入れられている範囲の外側にあるテスト
      • Source: (3), (5)
    • Test Case Timeout
      • 実行時間に上限が指定されたテスト。
      • Source: (3), (5)
  • Inter-test(他のテストの実行に関連して不安定となる)
    • Test Order Dependency
      • 他のテストによって変更される共有値や資源に依存し、その結果に影響するテスト
      • Source: (1), (2), (3), (4)
    • Resource Leak
      • 外部資源を不適切に扱うテスト(例:割り当てたメモリの解放に失敗する)
      • Source: (1), (2), (3), (4), (5)
    • Test Suite Timeout
      • 実行時間が制限されたテストスイートの一部であるテスト
      • Source: (3)
  • External(テストの制御外の要因)
    • Asynchronous Wait
      • 非同期呼び出しを行い、アサーションを評価する前にそれが終了するのを明示的に待たないテスト
      • Source: (1), (2), (3), (4), (5)
    • I/O
      • 入出力操作の扱いに起因してフレークとなるテスト。例えば、ディスクに空きがない、またはファイル書き込み中に満杯になると失敗するテスト
      • Source: (1), (3), (4), (5)
    • Network
      • ネットワーク接続の可用性に依存するテスト
      • Source: (1), (3), (4), (5)
    • Time
      • ローカルのシステム時刻に依存するテストで、精度やタイムゾーンの相違によりフレークになり得る
      • Source: (1), (3), (4), (5)
    • Platform Dependency
      • 特定のオペレーティングシステムの機能、ライブラリのバージョン、ハードウェアベンダ等の特定機能に依存するテスト。
      • Source: (1), (3), (4), (5)

上記の発生頻度の内訳は以下のようになります。

Owain Parry, Gregory M Kapfhammer, Michael Hilton, and Phil McMinn. 2021. A survey of flaky tests. ACM Transactions on Software Engineering and Methodology (TOSEM), 31, 1 (2021), 1–74. Table 5.のSourceを上記文章に合わせたもの
Flaky Testsの原因分類

-は、その研究が当該カテゴリを考慮しなかったことを示します。
割合は四捨五入されており、参照元の著者がすべてのFlaky Testsを分類できたわけではないため、合計は100%になりません。

研究ごとに分類元のプログラミング言語やデータソースが異なるため、ばらつきはありますが、どのような傾向があるかを掴むのには有効なまとめとなっています。

Flaky Testsの対策

再実行

2024年のCost of Flaky Tests in Continuous Integration: An Industrial Case Studyは、Flaky Testsのコストモデルを提示した論文で、実際のプロジェクトでの計測結果として「この研究の前提においては調査・修正よりも再実行を行う戦略の方がコスパがいい」という結論を出しています。

ただし、いくつかの注意点があります。
この論文ではFlaky Testsを見逃すことにより発生する本番障害のコストについては低く計上されています。

たとえば、いくつかの文献では、Flaky Testsが多い場合、それがプロダクトの故障を含んでいる可能性があることを示唆しています。

再実行は確かにコスパのいい方法ではありますが、それはFlaky Testを無視するのではなく、適切な監視や必要に応じての調査・修正とともに行う必要があると考えられます。

Flaky Testsを自動検知・修復する手法

Flaky Testsがプロジェクトに含まれているかどうかを、自動で検知するようなアイディア、さらにはそれを修復するアイディアが数多く考えられてはいます。
ここではそのいくつかを紹介します。

テストの順序性に起因する不安定さに注目した手法

Test Order Dependencyに起因するテストの不安定さを検出あるいは修復する手法について紹介します。

非決定的な実装に依存する不安定さの検出

たとえば、Hashのループの順番などの非決定的な実装を検出するための手法について紹介します。
プログラミング言語依存が強いため、特定言語以外での応用は難しいかもしれません。

効率よく Flaky Tests を見つける方法

特定の条件をつけたり、機械学習を用いて、テストの複数回実行を減らしてFlaky Testsを見つける方法について以下に列挙します。

まとめ

Flaky Tests と呼称して、この Flaky Tests の原因と対策について論じている文献についてまとめました。
Flaky Testsの対策は調査・修正するよりも再実行が安価である場合が多いですが、本番障害の不具合を見逃すリスクがあります。

Flaky Testsの自動で検知アイディアもあります。
Flaky Testsを検知する方法は実行コストの問題があります。事前予測については精度に問題があります。
また、自動修復するアイディアはありますが、現時点ですべてのFlaky testsを修復するものではありません。
検知も修復も現時点ではいくつかのツールがありますが、それは使用できる環境が限られている、あるいは、汎用的な手法であっても使用するのに環境にあった学習データの作成〜モデルの構築をする必要もあり、どのような環境でもすぐに使えるとは思えません。

Effective Software Testingの10.1.4 Tests should be repeatable and not Flakyでも、述べているようにしばらくは、問題のあるテストを見つけて修正するのは私たち自身の責任になるでしょう。

ただ、いままで紹介したFlaky Testsの原因や対策に対する文献を参考にしたアイディアで自分の環境にあった運用でFlaky Testsに対する対応環境を用意することは不可能ではないと思われます。

脚注
  1. xUnit Test Patterns: Refactoring Test CodeのChapter 16 Behavior Smellsで詳細が語られています。同様の内容がxUnit Patterns.com-Erratic Testでも確認できます。 ↩︎

  2. 最近は論文などでは"Flaky Tests"という表現が使用されています。最初期の文献 Google Test Blog TotT: Avoiding Flakey Testsなどでは"Flakey Tests"という表現がされています。現在では"Erratic Tests"や"Flakey Tests"と記述されるより"Flaky Tests"と記述されるケースが多いようです。 ↩︎

  3. Owain Parry, Gregory M Kapfhammer, Michael Hilton, and Phil McMinn. 2021. A survey of flaky tests. ACM Transactions on Software Engineering and Methodology (TOSEM), 31, 1 (2021), 1–74. ↩︎

  4. A. Alshammari, C. Morris, M. Hilton, and J. Bell. 2021. FlakeFlagger: Predicting Flakiness Without Rerunning Tests. In Proceedings of the International Conference on Software Engineering (ICSE). TableIIを和訳したもの
    テストのフレーク性予測のために取得した特徴の完全な一覧
    Test SmellsとはRefactoring Test CodexUnit test patterns: Refactoring test codeで提唱されたユニットテストの悪い兆候。
    このTest Smellsの検知にはASTノードを解析して検出している↩︎

  5. Qin, Y., Wang, S., Liu, K., Lin, B., Wu, H., Li, L., Mao, X., & Bissyandé, T. F. (2022). PEELER: Learning to Effectively Predict Flakiness without Running Tests. Proceedings of the 38th IEEE International Conference on Software Maintenance and Evolution (ICSME 2022), 257–268. IEEE. Figure1より
    Fig. 1: Overview of PEELER.
    具体的な実装方法についてはGitHubを参照 ↩︎

  6. Parry, O., Kapfhammer, G. M., Hilton, M., & McMinn, P. (2023). Empirically evaluating flaky test detection techniques combining test case rerunning and machine learning models (CANNIER). Empirical Software Engineering, 28, Article 72. Table 1 The 18 features measured by PYTEST-CANNIERより
    CANNIERの18の特徴
    CANNIERの18の特徴 ↩︎

Discussion