フレーキーテストとは? 原因と修正を解説
フレーキーテストとは、同じコードと同じ入力に対して異なる結果を返すテストであり、あるrunでは合格し、次のrunでは正当な理由なく失敗します。
フレーキーテストは、オンデマンドで再現できないため、最もたちの悪い種類のCI障害です。コードは変わらず、テストも変わらないのに、結果が反転します。時間が経つにつれ、開発者は赤いbuildを無視することを覚え、本物のリグレッションが気づかれずにすり抜けます。
「flaky」が実際に意味すること
テストがflakyであるとは、その結果が、検証すると主張する振る舞い以外の何かに依存している状態です。100回実行すると97回は合格し、3回の失敗はコードが壊れていると告げているわけではありません。テストスイートのS/N比が下がり、それが生み出すあらゆる結果への信頼も下がります。
よくある原因
- タイミングと非同期: 操作が終わる前にテストがアサートするため、マシンがたまたまどれだけ速いかに依存する。
- 共有状態: テストが互いにデータを漏らすため、順序や並列性が結果を変える。
- 外部依存: 実際のネットワーク呼び出し、時計、乱数シードが非決定性を持ち込む。
- リソース逼迫: 遅い、あるいはメモリ不足のrunnerが、本来問題のないテストをタイムアウトさせる。
フレーキーテスト vs 一時的なインフラ障害
どちらもflakinessに見える2つのものを区別する価値があります。フレーキーテストはテスト自体の問題で、テストかコードを変えることでのみ修正できます。一時的なインフラ障害(network blip、registryのタイムアウト、runnerでのout-of-memory kill)は環境的なもので、retryで修正できます。両者は異なる対応を要します。
チームがflakinessを抑え込む方法
持続的な修正は、テストを決定論的にすることです。実際のI/Oを取り除き、時間と乱数を制御し、状態を分離します。それまでは、チームは既知のflakyなテストを隔離してmergeをブロックしないようにし、flakyテスト率を追跡して問題を無視せず可視に保ちます。
Latchkeyの観点
Latchkeyの自己修復するマネージドrunnerは、本当にflakyなテストを代わりに修正することはできませんが、flakinessに偽装する環境的なノイズは取り除きます。network blip や out-of-memory kill のような一時的または機械的な問題でjobが失敗したとき、runnerがそれを検出して自動でretryするため、一度きりのblipがbuildを失敗させません。その結果、実際にテストのバグである、より小さく明確な障害の集合が残ります。
重要なポイント
- フレーキーテストは、同一のコードと入力に対して成功したり失敗したりします。
- 原因は通常、タイミング、共有状態、外部依存、またはリソース逼迫です。
- フレーキーテスト(テストのバグ)は、一時的なインフラ障害(retry可能)とは異なります。
- テストを決定論的にし、残りは隔離して測定します。