フレーキーテスト

フレーキーテストとは

フレーキーテスト(flaky test)は、コードを変えていないのに結果が変わるテストです。10回に1回落ちる、CI では落ちるが手元では通る、Firefox だけ落ちる、といった形で現れます。

厄介なのは、テストが信号として機能しなくなることです。赤を見ても「またあれか」と再実行で済ませる習慣がつくと、本物の回帰が混じっても見過ごされます。[[CI/CD]] のパイプラインにおいて、フレークは「たまに落ちる」以上の損失です。

なぜ起きるか — 4つの原因

原因典型例
タイミング描画や非同期処理の完了を固定時間の sleep で待っている。マシンが遅いと間に合わない
共有状態・実行順序前のテストが残した localStorage やキャッシュ、DB の行が次のテストに影響する。並列実行や順序の入れ替えで顔を出す
資源の逼迫別のジョブと同時に走って CPU を取り合い、タイムアウト境界のテストが落ちる
外部依存実ネットワークや実サービスを叩いている。相手の遅延や停止で結果が変わる

多くの場合、テストは「壊れている」のではなく前提を暗黙に置いているだけです。「この処理は5秒で終わる」「前のテストは何も残さない」「ネットワークは速い」といった前提が、環境によって崩れます。

再実行で済ませてはいけない理由

再実行は症状を隠すだけで、次の3つを引き起こします。

  • 本物の回帰を見落とす — フレークと本物の失敗は同じ赤で表示される。再実行の習慣は両方を無視する習慣になる
  • 後続の工程が止まる — 失敗すると後段のジョブ(デプロイや同期)が skipped になる構成では、フレーク1件で本番への反映が止まる。しかも「落ちた」ことは見えても「反映されなかった」ことは別の検査で初めて分かる
  • コストが積み上がる — E2E の再実行は数十分単位。並行して走る PR が増えれば、CI の実行枠や課金上限に達する

「原因を突き止めて直す」か「原因が分かるまで隔離する」かのどちらかを選び、無言の再実行は選択肢から外します。

切り分けの手順 — 「単独で通った」は根拠にならない

フレークの調査で最も多い誤判定が、単独実行で通ったから環境要因だと結論することです。単独でも実行のたびに通ったり落ちたりするのがフレークなので、1回通った事実は何も証明しません。

決め手は実測です。

  1. 所要時間を測る — 落ちるテストの各ステップ(初期化、遷移、待機)に計測を仕込み、内訳をログに出す。「初期化だけでタイムアウトの4割を使っていた」のような事実が出てくる
  2. 環境ごとの差を見る — 同じテストを chromium / firefox / webkit で測ると、特定のブラウザだけ数倍遅いことがある。その場合は実装の回帰ではなく、待ち時間の前提が崩れている
  3. 基準と比べる — 変更前のコミットを別の作業ツリーに展開し、同じ条件で回す。基準でも同じ頻度で落ちるなら、今回の変更が原因ではない
  4. 同時に走っているものを確認する — レビュー用の並列ジョブや別のビルドと同時なら、まずそれを止めて単独で回す

「たぶんこれだろう」で対処を始めると、直したつもりで別の要因が残ります。数字を1つ取ってから直します。

直し方

  • 時間ではなく条件を待つ — sleep(3000) ではなく「要素が表示されるまで」「リクエストが完了するまで」を待つ。[[Playwright]] の自動待機はこのために存在する
  • テストごとに状態を初期化する — localStorage・Cookie・キャッシュ・DB を各テストの前にリセットする。保存先を1つ増やしたら、リセット処理にも1つ足す
  • タイムアウトは根拠を持って決める — 実測した所要時間に余裕を乗せる。遅いブラウザや重い画面だけ個別に伸ばす設定を使い、全体を一律に延ばさない
  • 外部依存を差し替える — 単体・結合の層では [[テストダブル(モック・スタブ)]] で外部を切る。E2E で実サービスを叩くなら、その揺れを織り込んだ判定にする
  • 隔離には期限を付ける — 原因が分からないテストを skip にするなら、Issue 番号と期限をコメントに残す。無期限の skip は「消したテスト」と同じ

初学者向けポイント

  • 「たまに落ちる」と聞いたら、まず落ちる頻度と落ちる環境を記録します。頻度が分からないと、直ったかどうかも分かりません
  • 固定時間の待ちをテストに書いた瞬間、そのテストはフレークの候補です。条件で待てないか考えましょう
  • 手元(Windows)と CI(Linux)で結果が違うときは、改行コードなど環境差そのものが原因のこともあります([[改行コード(CRLF・LF)]])

関連技術とのつながり

  • [[自動テスト]] — テスト層の全体像。フレークは外側の層ほど起きやすい
  • [[Playwright]] — 自動待機と、プロジェクト単位のタイムアウト設定
  • [[CI/CD]] — フレークがパイプライン全体を止める構造
  • [[テスト駆動開発]] — 先に書くテストが決定的であるほど、後からのフレークが減る
  • [[テストダブル(モック・スタブ)]] — 外部依存という揺れの源を切る道具
  • [[並行処理と非同期]] — 並列実行で顔を出す共有状態の問題
  • [[改行コード(CRLF・LF)]] — 環境要因と誤認されやすい失敗の実例
Q: フレーキーテストの説明として正しいものはどれ?
- [ ] 実装のバグで毎回必ず落ちるテスト
- [x] コードを変えていないのに、通ったり落ちたりするテスト
- [ ] 実行に時間がかかるテスト
解説: 結果が実行ごとに変わり、赤が信号として機能しなくなるのが問題の本質です。

Q: 「単独で実行したら通った」から環境要因だと結論してはいけない理由はどれ?
- [x] フレークは単独でも実行ごとに結果が揺れるため、1回通った事実は何も証明しないから
- [ ] 単独実行は CI と違うコマンドを使うから
- [ ] 単独実行ではテストがスキップされるから
解説: 決め手は所要時間などの実測です。ステップごとの内訳を測り、基準のコミットと比べて切り分けます。

Q: フレークへの対処として本文が推奨しているものはどれ?
- [ ] 落ちたら無言で再実行する
- [ ] タイムアウトを全体で一律に10倍にする
- [x] 固定時間の待ちを条件の待ちに変え、テストごとに状態を初期化する
解説: 再実行は症状を隠すだけで、一律の延長は根拠がありません。実測に基づいて待ち方と初期化を直します。