不安定なテスト:原因と根本的な解決策

Yunhao Jiao
不安定なテスト:原因と根本的な解決策 カバー

不安定なテスト(Flaky Tests)は、あらゆる自動テストプログラムに課せられる見えないコストです。断続的に失敗し、再実行すると通過し、エンジニアリングチームに選択を迫ります。「おそらく本物ではない失敗」の調査に時間を費やすか、無視して「本物だった失敗」を見逃すリスクを負うか。

多くのチームは結局どちらも行い、どちらも一貫して実施できません。その結果、エンジニアが信頼できなくなったCIパイプラインと、蓄積されたノイズによってほぼ無意味になった品質シグナルが残ります。

このガイドでは、不安定なテストの原因、体系的な診断方法、そして症状を一時的に隠すのではなく根本原因を排除する最新AIテストアプローチについて説明します。

不安定なテストとは?

不安定なテストとは、同じコードに対して実行しても結果が一定しない自動テストのことです。パスしたり失敗したりします。このテストの失敗はアプリケーションの本物のバグによるものではなく、テスト自体の問題によるものです。タイミングの問題、環境への依存、共有ステート、またはテスト実行における非決定性が原因として挙げられます。

不安定なテストは、正当に失敗するテストとは異なります。正当な失敗はアプリケーションにバグがあることを意味します。不安定なテストの失敗はテスト自体にバグがあることを意味します。より一般的には、テストがタイミング、ステート、または環境について信頼性のない前提を持っています。

不安定なテストが見た目以上に有害な理由

不安定なテストの明白なコストは、CIパイプラインの再実行と誤検知の調査に費やされる時間です。これは現実のコストであり、チームは不安定なテストのノイズ管理に毎週数時間を無駄にすることがあります。しかし、それが最も深刻な問題ではありません。

より深刻な問題はシグナルの劣化です。CIパイプラインが頻繁に赤くなると、エンジニアは赤を意味のあるものとして扱うのをやめます。まず再試行し、後で調査し、再試行が通れはマージするという習慣が身につきます。「おそらく不安定なテストだろう」という文化は、本物の失敗も無視されることを意味します。リグレッションがマージされます。バグが出荷されます。

チームがテストスイートを信頼できなくなると、その信頼を回復するには多大な労力が必要です。根本的な不安定さを修正するよりもはるかに大きな労力が。

不安定なテストの7つの根本原因

1. タイミングと競合状態

最も一般的な不安定なテストの原因です。テストが準備完了前に要素やAPIと対話します。レンダリングが完了していないボタン、レスポンスが返ってきていないAPI、完了していないアニメーションなどです。固定待機(sleep(2000))は信頼性が低く低速なため、状況をさらに悪化させます。正しい解決策は、実際のアプリケーションの状態を監視するアダプティブウェイトです。

兆候:低速なマシンではテストが通過し、高速なマシンでは失敗します。ローカルでは通過するがCIでは失敗します。パフォーマンス最適化後にテストが失敗し始めます。

2. テスト間の共有ステート

テストBはテストAによって作成されたステートに依存しています。テストが異なる順序で実行されたり、テストAがスキップされたりすると、テストBはテスト対象のコードとは無関係な理由で失敗します。テストは完全に分離されているべきです。各テストは自身のステートを作成し、後始末を行います。

兆候:テストは単独では通過するが、完全なスイートの一部として実行すると失敗します。他のどのテストが実行されるかによって失敗が変わります。

3. 外部依存

サードパーティAPI、データベース、または外部サービスへの実際のネットワーク呼び出しを行うテストは、それらのサービスが遅い、レートリミットに達している、または一時的に利用できない場合に失敗します。これらの失敗は環境の失敗であり、アプリケーションの失敗ではありません。

兆候:特定の時間帯にテストがより多く失敗します。失敗メッセージにネットワークタイムアウトや429レートリミットレスポンスが含まれています。

4. 環境の不一致

テストがある環境では通過し、別の環境では失敗します。Nodeのバージョンの違い、タイムゾーン設定の違い、データベースのシーディングの違い、環境変数の違いなどです。テストが普遍的に真ではない環境に関する前提を持っています。

兆候:ローカルでは通過するがCIでは失敗します。あるエンジニアのマシンでは通過するが別のマシンでは失敗します。

5. 脆弱なロケーター

CSSセレクターやXPath式を使用するUIテストは、変更がコスメティックで機能に影響しない場合でも、DOMが変更されると失敗します。開発者がクラス名を変更したり、フレームワークのアップデートがレンダリングされたHTML構造を変更したり、デザインシステムのトークンがコンポーネントのマークアップを変更したりします。

兆候:ユーザー向けの動作を変更しないUIリファクタリング後にテストが壊れます。同じコンポーネントを中心に失敗が繰り返し集中します。

6. 実行順序への依存

共有ステートと関連しますが、特にテストの順序に関するものです。一部のテストランナーは実行ごとに異なる順序でテストを実行し、順序に関する暗黙的な前提を持つテストは順序が変わったときに不安定になります。

兆候:コードが変更されていないのに実行間でテストの失敗が変わります。特定のテストが常に一緒に失敗します。

7. アプリケーション自体の非決定的な動作

アプリケーション自体が同じ入力に対して異なる出力を生成します。ランダムなID、タイムスタンプ、確率的なAI出力、ランダム化されたソート順などです。これらの出力をアサートするテストは、厳密な値ではなく意図をテストしない限り不安定になります。

兆候:テストがタイムスタンプ、生成されたID、その他の可変出力を含む正確な値をアサートしています。

不安定なテストの診断方法

ステップ1:無視するのではなく隔離する。不安定なテストを別途タグ付けします。既知の不安定なテストとしてマークし、CIのブロッキングから除外します。これにより根本原因を修正する間、シグナルの劣化問題を止められます。削除するのは誤りです。多くの場合、実際の機能をカバーしています。

ステップ2:不安定なテストを単独で繰り返し実行する。安定したコードベースに対して問題のテストを20〜50回実行します。これにより実際に不安定であることを確認し、失敗率データを取得できます。

ステップ3:実行間で何が変わるかを調べる。タイミング?テストの順序?環境?失敗メッセージには通常手がかりが含まれています。ネットワークタイムアウトは外部依存の問題を指します。要素が見つからないはタイミングまたはロケーターの問題を指します。ステートの不一致は共有ステートを指します。

ステップ4:個別のインスタンスではなくカテゴリを修正する。sleepを追加して単一の不安定なテストを修正するのは誤りです。問題のカテゴリを修正します。アダプティブウェイトを体系的に使用し、ステートを体系的に分離し、外部依存を体系的にモックします。

AIテストツールが根本から不安定さを排除する方法

従来の自動テストでは、上記のすべての修正をエンジニアが手動で実装する必要があります。つまり、適応型ウェイトの記述、状態の分離、依存関係の適切なモック化などです。これは手間がかかり、エラーが発生しやすい作業です。

TestSprite などのモダンなAIテストツールは、不安定性をアーキテクチャレベルで解決します。

インテントベースのロケーターが、壊れやすいCSSセレクターに取って代わります。`cy.get('.submit-btn-v3')` のような記述の代わりに、AIが「フォームのメイン送信ボタン」という意図を実行時にセマンティックにマッチングします。マークアップが変更されても意図は有効なままであるため、UIのリファクタリングによってテストが壊れることはありません。

インテリジェントな失敗分類により、テストの脆弱性と本物のバグを自動的に区別します。テストが失敗した場合、TestSprite のエンジンはその失敗がアプリケーションの本物のバグによるものか、タイミングやロケーターの問題か、あるいは環境の問題かを判断し、それぞれに適切に対処します。本物のバグは実行可能なレポートとして表面化され、脆弱性は自動修復されます。

隔離されたクラウドサンドボックスにより、環境の不一致を完全に排除します。すべてのテスト実行は既知のクリーンな状態から開始されます。共有データベースも、前回の実行による残存状態も、ローカル環境の差異もありません。

アダプティブ実行により、固定ウェイトなしでタイミングや競合状態を処理します。TestSprite は各テストステップを進める前に、DOMの安定性、ネットワークのアイドル状態、アニメーションの完了など、実際のページ状態を監視します。

実際の効果として、TestSprite を使用するチームが劇的にフレーク率が低下したと報告しています。これは、AIテストエンジンが不安定性の根本原因を単に検出して再試行するのではなく、回避するよう設計されているためです。

フレークフリーな文化の構築

ツールの活用に加えて、フレーキーなテストへの対処においてチームができる最も重要なことは、それを本物のバグとして扱うことです。フレーキーなテストはテストスイートにおける欠陥であり、許容できる問題ではありません。トリアージ、根本原因分析、そして恒久的な修正など、本番バグと同等の注意が必要です。

この基準を一貫して維持するチームは、エンジニアが信頼できるテストスイートを持ちます。そして、エンジニアが信頼するテストスイートこそが、実際にリグレッションを検出します。

TestSprite でフレークフリーなテストスイートの構築を始める →