01

1台のRunnerで起きる制約

症状:matrixでJobを増やしたのに、iOS UIテストの待ち時間が短くならない。
最短解決策:Xcode Test Plansまたはonly-testingで安定した境界を先に作り、複数の独立したリモートMacへmatrixを振り分けます。

self-hosted Runnerが1台しかない場合、matrixの組み合わせを増やしても、同時に処理できるJobはそのRunnerの空き状況に制限されます。GitHub ActionsのJob実行とRunnerへの割り当ての関係は、GitHub Actionsの公式概要で確認できます。

この制約を見落とすと、設定上の並列数だけが増え、実際にはキューが長くなります。単一Mac内のXcode Simulator並列、複数Macのmatrix並列、Swift Testingのプロセス内並列は、別々の拡張軸として測定してください。

このガイドは、XCTestのUIスイートを分割したいテストエンジニア、Runnerタグと同時実行制御を担当するDevOpsエンジニア、ノード追加の判断をする開発責任者向けです。すでにMacを購入するか迷っている場合は、Mac miniの運用方法を比較するガイドも判断材料になります。

02

基線データと分割境界

実行時間を構成要素に分ける

最初に、完全なUIテストを分割せずに実行します。記録する項目は次のとおりです。

  • ソース取得と依存関係解決の時間
  • ビルド、インストール、Simulator起動の時間
  • テストスイートごとの実行時間
  • 失敗したテスト名と再現位置
  • Runnerの待機時間
  • xcresult、ログ、スクリーンショットの保存状況

Appleはテストの実行と結果の読み取りについて、テスト結果バンドルを含む手順を公開しています。Appleのテスト実行と結果確認の資料を基準に、ビルド時間をUIテスト時間へ合算しないでください。

次に、失敗位置とデータ依存を確認します。ログイン状態、共有アカウント、プッシュ通知、決済状態、連続した業務フローを使うテストは、ファイル数だけで分けると壊れやすくなります。

分割単位を固定する

最初の分割では、テストケース数を均等にするより、依存関係のない境界を優先します。Xcode Test Plans、Test Target、Suite、only-testingのいずれかを選び、各分割に次の情報を持たせます。

管理項目 分割ごとに固定する内容 確認方法
識別子 ui-authui-purchaseなどの占位名 Job名と成果物名を一致させる
対象 Scheme、Test Plan、only-testingの範囲 全量テストと照合する
入力 テストアカウント、環境変数、初期データ Jobごとに明示する
出力 xcresult、ログ、画像の保存先 分割間で上書きしない
復現 単独で再実行できるコマンド ローカルまたは指定Runnerで確認する

Appleのテスト整理に関する公式資料でも、フィードバックを改善するにはテストの構成と実行単位を整理することが重要とされています。分割後は、全量実行の結果と比較し、未実行と重複実行を確認します。

03

matrix接続とノード分離

GitHub ActionsのJob設計

matrixでは、分割識別子を入力値にします。実際のリポジトリ名、Runnerラベル、Scheme、Test Plan、端末名、アカウント、パスは固定値を直書きせず、あなたの環境の占位名へ置き換えてください。

strategy:
  fail-fast: false
  matrix:
    shard: [ui-auth, ui-purchase, ui-settings]

runs-on: [self-hosted, macos, ios-ui]

これは設定例の骨格であり、Runnerの台数を増やす設定ではありません。max-parallelはJobの展開数を制御しますが、空きRunnerがなければ実行待ちになります。concurrencyは同じ環境への重複Workflowを止める仕組みであり、処理能力を増やす仕組みではありません。Workflow構文の公式リファレンスで、matrixとconcurrencyの役割を分けて確認してください。

Runner側では、Xcode、Simulator、プロジェクト権限、証明書の有無が同じ条件になるようにします。ラベルとRunner Groupによる割り当ては、self-hosted Runnerの公式手順に従って設定します。

成果物と一時領域の分離

分割Jobごとに、少なくとも次のパスを変えます。

  • Simulatorの名前またはdestination
  • DerivedData
  • xcresultの出力先
  • 一時ディレクトリ
  • 起動するローカルポート
  • テストアカウントと初期データ

1台のMacでXcodeの並列テストを試す場合も、別の分割Jobが同じSimulatorやDerivedDataを使わないようにします。単一ノード内の並列化は、複数Macへの分散とは異なり、CPU、メモリ、ストレージI/Oを共有します。

実行方式 増えるもの 主なリスク 適する段階
1台で直列 再現性 待ち時間 最初の基線
1台で内部並列 1台の処理密度 Simulatorと共有状態の競合 依存関係を除去した後
複数Macでmatrix 独立した実行枠 Runner不足、環境差 分割を検証した後
直列+並列の双軌 比較可能性 実行コストと管理量 公開前の安全確認

最初から最大並列を目指さないでください。まず分割を2つ程度の小さな単位で試し、1回の変更では並列化する層を1つだけ変えます。システムリソース、テストログ、スクリーンショットを同時に確認すると、資源不足とテストコードの共有状態を切り分けやすくなります。

04

分割結果の観測と判断

成果物の集約

各Jobは、次の成果物を独立してアップロードします。

  1. xcresult
  2. コンソールログ
  3. スクリーンショットまたは動画
  4. 分割識別子
  5. 使用したScheme、Test Plan、destination
  6. 終了コードと失敗理由

集約Jobでは、最初に全分割がそろっているか確認します。その後、インフラ障害、Runner障害、Simulator起動失敗、アプリのアサーション失敗、不安定テストに分類します。

再試行は原因を調べるために使います。2回目が成功しても、1回目の失敗を成功扱いに上書きしないでください。失敗履歴を残し、隔離リストと担当チームを明確にします。カバレッジも分割ごとの割合を単純に加算せず、対象の重複と公式ツールでの統合可否を確認します。

観測項目 継続の判断 差し戻し条件
最長分割の実行時間 分割境界を再調整 1つの分割だけが常に全体を支配
Runner待機 ノード追加を検討 実行時間より待機が長い
失敗再現率 不安定テストを隔離 分割変更で再現不能になる
Simulator状態 同一条件を維持 データやポートが相互汚染する
結果欠落 集約処理を修正 失敗判定が欠落する

条件分岐による運用決定

  • もし待機時間が主な遅延で、各分割が単独で安定しているなら、複数のリモートMacへmatrixを配分します。そうでなければノード追加を保留します。
  • もし同じテストが別分割のデータやアカウントを参照しているなら、依存するテストを同じ分割へ戻します。そうでなければ分割境界を維持します。
  • もし単一Macの内部並列でSimulatorやDerivedDataが競合するなら、並列度を下げて直列基線へ戻します。そうでなければ低い並列度だけを継続検証します。
  • もし公開前の結果が分割実行と一致しないなら、直列の全量テストを合格条件に残します。そうでなければ並列結果を通常のPR検証へ広げます。
  • もし失敗がテストコードではなくノード環境に集中するなら、Xcode、Simulator、権限、ディスク状態をそろえます。そうでなければテスト側の共有状態を調べます。
05

初回運用で確認する項目

最初の実運用では、連続するPull Requestを使って次を記録します。

  • Workflow開始から最終結果までの時間
  • Runnerのキュー待機
  • 各分割の偏り
  • 初回失敗と再試行後の差
  • ノードのCPU、メモリ、ディスクI/O
  • xcresultの欠落
  • 直列全量テストとの結果差

最長分割が毎回全体時間を支配するなら、さらに細かく分けるか、Suiteの構成を見直します。逆にRunner待機が支配的なら、matrixの数を増やす前に空きノードを増やす判断が必要です。

CI用のMacを購入する場合は、常時利用する物理機、保守担当、故障時の交換手順まで必要になります。短期の検証やリリース前だけ必要な場合は、KVMNODEのMac環境のようなリモートMacを候補に加えると、実機を固定資産化せずに同じ手順を試せます。なお、物理ポートが必要な端末試験や、長期間の高負荷処理を常時行う用途では、自前のMacのほうが適する場合もあります。

06

FAQ

GitHub ActionsでiOS UIテストを並列化する構成

複数のiOS UIテストスイートを同時に動かすには、まずテストの独立性を確認します。次にmatrixで分割ごとのJobを作り、適切なラベルを持つRunnerへ割り当てます。matrixの行数は実行枠の数ではないため、空きRunnerの台数、max-parallel、Workflowの重複制御を同時に確認してください。

1台のMac RunnerとSimulatorの同時実行

1台のMac Runnerで動かせるSimulator数は、Xcodeのバージョン、端末構成、アプリの負荷、メモリ、ディスクI/Oによって変わります。固定値を前提にせず、Simulator、DerivedData、結果パス、アカウントを分離し、低い並列度から試します。失敗率が上がる場合は、並列化より直列基線を優先します。

Test Planを使ったUIテストの分割

Xcode Test Plansでは、対象となるテストや設定をまとまりとして管理できます。分割後は、各Planまたはonly-testingの範囲を独立実行し、全量実行の一覧と照合します。ファイル数の均等化だけで決めると、ログインや連続操作の依存関係を壊すため、状態境界を先に確認してください。

複数xcresultの失敗箇所

分割ごとに異なるxcresult名を付け、ログとスクリーンショットも同じ識別子で保存します。集約時は、欠落した分割を最初に検出し、その後でテスト失敗と環境失敗を分けます。カバレッジは分割の数値を足し算せず、対象の重複を確認してから公式の結果解釈手順に沿って判断します。

07

公開前の双軌運用

公開に関わる重要なUIテストは、並列分割だけへすぐに置き換えないでください。まず直列の全量テストを基準として残し、分割実行との結果一致、失敗の再現性、成果物の完全性を確認します。

あなたの現在の構成が1台のMacに集中している場合、待機時間だけでなく、Simulatorの共有状態、ディスク競合、障害時の復旧時間も負担になります。物理Macの購入は初期費用と保守を引き受ける必要があり、単一のクラウド環境だけではmacOS専用のXcode検証を代替できません。

先に単一ノードの基線と小規模な2分割を試し、遅延の原因がRunner不足だと確認できた段階で、KVMNODEのリモートMacを一時的な独立テストノードとして検討してください。短期の検証、リリース前の増枠、複数環境の比較では、必要な期間だけMacを確保できる構成のほうが、購入後に使わない実機を抱えるより判断しやすくなります。