mcpField
F
fix-flaky-tests

fix-flaky-tests

github.com/tuistテスト・品質35505 回インストール31 週間前に更新
リポジトリ

fix-flaky-tests について

Fix Flaky Testsは、断続的に失敗するテストを調査し、対象を絞ってコードを修正し、繰り返し実行して確認するワークフロースキルです。TuistのテストデータやXcodeのテスト実行を扱う開発者向けです。

主な機能

  1. 01不安定と判定されたテストの検索
  2. 02信頼性指標と実行履歴の確認
  3. 03失敗内容とソース位置の確認
  4. 04よくあるタイミングや状態の問題の特定
  5. 05テストの反復実行による修正確認

活用シーン

  1. 01プロジェクト内の不安定なテストのトリアージ
  2. 02失敗箇所からテストのソースを特定
  3. 03反復または並列実行で修正を確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/tuist/agent-skills --skill fix-flaky-tests

インストール後、Claude Code で/fix-flaky-testsを入力して読み込まれたか確認します。

詳細

不安定なテストの修正

クイックスタート

通常、Tuist のテストケースの URL または識別子を受け取ります。次の手順で調査し、修正してください。

  1. tuist test case show <id-or-identifier> --json を実行して、テストの信頼性メトリクスを取得します。
  2. tuist test case run list Module/Suite/TestCase --flaky --json を実行して、不安定な実行のパターンを確認します。
  3. 失敗した不安定な実行に対して tuist test case run show <test-case-run-id> --json を実行し、失敗メッセージとファイルパスを取得します。
  4. 報告されたパスと行にあるテストソースを読み、不安定なパターンを特定して修正します。
  5. テストを複数回実行し、安定して成功することを確認します。

特定のテストが指定されていない場合は、以下の「検出」セクションから始めてください。

検出

特定のテストケースが指定されていない場合は、プロジェクト内のすべての不安定なテストを見つけます。

tuist test case list --flaky --json --page-size 50

これにより、現在不安定と判定されているすべてのテストケースが返されます。主なフィールドは次のとおりです。

  • module.name / suite.name / name — テスト識別子
  • avg_duration — 優先順位付けに役立ちます(まず高速なユニットテストを修正します)
  • is_quarantined — テストがすでに隔離されているかどうか

トリアージ戦略:

  1. テストをスイートごとにグループ化します。同じスイート内に不安定なテストが複数ある場合、共通の根本原因があることがよくあります。
  2. 失敗が同じ test_run_id を共有しているか確認します。同じ実行で失敗したテストは、個々のテストの不具合ではなく、プロセスのクラッシュによって終了した可能性があります。
  3. 失敗メッセージを確認して分類します。テストロジックの不具合か、インフラストラクチャの問題(ネットワークエラー、サーバーの 502 エラー、再試行時の競合)かを見極めます。

調査

1. テストケースのメトリクスを取得する

UUID または Module/Suite/TestCase 形式の識別子を渡せます。

tuist test case show <id> --json
tuist test case show Module/Suite/TestCase --json

主なフィールドは次のとおりです。

  • reliability_rate — 成功した実行の割合(高いほど良好です)
  • flakiness_rate — 過去 30 日間に不安定と判定された実行の割合
  • total_runs / failed_runs — 実行数の参考情報
  • last_status — 現在の状態
2. 不安定な実行履歴を確認する
tuist test case run list Module/Suite/TestCase --flaky --json

識別子には ModuleName/SuiteName/TestCaseName 形式を使用します。スイートがない場合は ModuleName/TestCaseName 形式を使用します。不安定と検出された実行のみが返されます。

3. 実行履歴全体を確認する
tuist test case run list Module/Suite/TestCase --json --page-size 20

次のようなパターンを探します。

  • 特定のブランチで失敗していますか?
  • CI(is_ci: true)でのみ失敗しますか、それともローカルでも失敗しますか?
  • 失敗は特定のコミット前後に集中していますか?
4. 失敗の詳細を取得する
tuist test case run show <test-case-run-id> --json

主なフィールドは次のとおりです。

  • failures[].message — アサーションまたはエラーメッセージ
  • failures[].path — ソースファイルのパス
  • failures[].line_number — 失敗した正確な行
  • failures[].issue_type — 問題の種類(assertion_failure など)
  • repetitions — 存在する場合は、再試行の動作(成功/失敗の順序)を示します
  • test_run_id — この実行が属する、より大きなテスト実行
  • crash_report — クラッシュレポートデータ(テストランナーがクラッシュした場合に存在します)。exception_type、signal、exception_subtype、triggered_thread_frames が含まれます

コード分析

  1. failures[0].path のファイルを開き、failures[0].line_number に移動します。
  2. テスト関数全体と、そのセットアップ/ティアダウンを読みます。
  3. 以下のよくある不安定なパターンのどれに該当するかを特定します。
  4. 同じスイート内のほかのテストと状態を共有していないか確認します。

よくある不安定なパターン

タイミングと非同期処理の問題
  • 待機の不足:非同期処理の完了前にテストが結果を確認しています。修正方法:await、タイムアウト付きの期待値、またはポーリングを使用します。
  • 競合状態:複数の並行処理が共有状態にアクセスしています。修正方法:アクセスを同期するか、直列キューを使用します。
  • タイムアウトのハードコード:sleep(1) や固定の遅延が CI では短すぎます。修正方法:固定の遅延ではなく、条件に基づく待機を使用します。
共有状態
  • テストの汚染:あるテストがグローバル状態または静的状態を変更し、別のテストがその状態に依存しています。修正方法:setUp/tearDown で状態をリセットするか、テストごとに固有のインスタンスを使用します。
  • シングルトンの汚染:共有シングルトンがテスト間で状態を保持しています。修正方法:依存関係を注入するか、シングルトンをリセットします。
  • ファイルシステムの残留物:テストが後続の実行に影響するファイルを残しています。修正方法:一時ディレクトリを使用して、後片付けを行います。
環境依存
  • ネットワーク呼び出し:テストでは、応答が遅い、または利用できない可能性がある実際のサービスにアクセスします。対処法:ネットワーク呼び出しをモックします。
  • 日時への依存:テストが現在の時刻やタイムゾーンに依存します。対処法:時計を注入するか、時刻を固定します。
  • ファイルシステムのパス:環境によって異なるハードコードされたパスです。対処法:相対パスまたは一時ディレクトリを使用します。
実行順序への依存
  • 暗黙の順序依存:必要な状態を設定する別のテストの後に実行した場合のみ、テストが成功します。対処法:各テストを自己完結させます。
  • 並列実行時の競合:単独では動作するテストが、同時に実行すると失敗します。対処法:テストごとに一意のリソースを使用します。
クラッシュ(crash_report で特定)
  • EXC_BREAKPOINT / SIGTRAP — nil の強制アンラップ、Swift の事前条件違反
  • EXC_BAD_ACCESS / SIGSEGV — 解放後の使用、またはダングリングポインター
  • EXC_CRASH / SIGABRT — 捕捉されていない Objective-C 例外

修正の実装

パターンを特定したら、次の手順を実行します。

  1. 根本原因に対処する最小限の修正を適用します。
  2. 関連のないコードはリファクタリングしないでください。
  3. モックやヘルパーなどのテスト用ユーティリティが必要な場合は、新しく作成する前に既存のものがないか確認します。

検証

テストの繰り返し実行

xcodebuild に組み込まれている繰り返し実行機能を使って、失敗するまで特定のテストを繰り返し実行します。

xcodebuild test -workspace <workspace> -scheme <scheme> -only-testing <module>/<suite>/<test> -test-iterations <count> -run-tests-until-failure

これにより、テストは最大 <count> 回実行され、最初の失敗時に停止します。テストにかかる時間に応じて反復回数を選択してください。高速なユニットテストの場合は 50~100 回、低速な統合テストや受け入れテストの場合は 2~5 回にします。

修正前の再現

修正を適用する前に、不安定な失敗をローカルで再現できるか試してください。再現に成功すると、根本原因の分析を裏付けられ、修正を直接検証できます。上記の「テストの繰り返し実行」の方法を使うか、並行処理が疑われる場合は、以下の競合状態の再現方法を使ってください。

特に競合状態、CI 固有のタイミングの問題、環境依存の失敗など、一部の不安定なシナリオはローカルでの再現が困難、または不可能な場合があります。妥当な範囲で試しても再現できない場合は、コード分析と失敗ログに基づいて修正を進めてください。明確な不具合の証拠(例:同期されていない共有状態、TOCTOU パターン)に裏付けられた修正であれば、ローカルで再現できなくても有効です。

競合状態の再現

競合状態や並行処理のバグは、CI レベルの並列実行でのみ発生することが多く、ローカルでの再現が困難です。次の方法を順に試してください。

  1. 並列度を上げる:-parallel-testing-enabled YES を追加して、テストスイートを並行して実行します。
  2. より広範なテストスイートを実行する:単一のテストではなく、モジュール全体(例:-only-testing ModuleTests)を実行し、共有リソースの競合を増やします。
  3. スレッドサニタイザー:TSan を有効にして実行し、データ競合を確実に検出します。注:TSan によるオーバーヘッドでタイミングが変化し、TSan の実行時には一部の競合が発生しない場合があります。
xcodebuild test -workspace <workspace> -scheme <scheme> -only-testing <module> -enableThreadSanitizer YES
  1. 広い範囲での高い反復回数:上記の方法をすべて組み合わせ、並列実行と多くの反復回数でモジュール全体を実行します。

ローカルで競合状態を再現できなくても、コードがスレッドセーフでないことが明らかな場合(例:共有状態を同期せずに変更している場合)は、修正は有効です。上記と同じ再現方法でテストが成功することを確認し、修正を検証してください。コミットメッセージには、コード分析と失敗ログに基づいて特定した、CI でのみ発生する競合状態への対処であることを記載してください。

完了チェックリスト

  • 不安定さの根本原因を特定した
  • 対象を絞った修正を適用した
  • テストが一貫して成功することを(複数回実行して)検証した
  • 新たなテスト依存関係や共有状態を導入していない
  • 内容を適切に説明するメッセージを付けて修正をコミットした

コメント