Appleの公式資料では、Xcodeのテストをコマンドラインから実行し、テスト結果を結果パッケージとして保存できることが確認されています。Appleのコマンドラインテスト資料に沿って、最初は「1つのScheme、1つのdestination、1つのSimulator Runtime」に絞ってください。結論として、すべての開発作業をMacへ移す必要はありません。ローカルでコーディングし、リモートMacをiOS自動化テストサーバーとして使う二環境構成が、独立開発者には現実的です。
今週は、単一テストの実行とxcresultの回収までを完了させてください。毎日の回帰テストや夜間実行が必要になってから、常駐運用や複数シミュレーターを検討すれば十分です。
SECTION 01 対象者と到達点
この記事は、WindowsまたはLinuxでクロスプラットフォームのコードを書きながら、iOSのXCTestやUIテストを実行したい個人開発者向けです。日常の開発機から重いテスト処理を切り離したい単独開発者、複雑なCI基盤をまだ必要としない小規模チームにも適しています。
ここで作るのは、ソースコードを編集する場所ではなく、テストを受け取り、Simulatorで実行し、診断用の成果物を返す環境です。署名やApp Store公開の手順まで一度に広げず、テストサーバーとしての再現性を先に確認します。
SECTION 02 構築前の環境分離
ローカル側とリモート側
ローカル環境にはエディター、Git操作、レビュー、日常的なコード修正を置きます。リモートMacにはXcode、Command Line Tools、対象のSimulator Runtime、依存関係、DerivedData、テスト結果を置き、SSHなどでコマンドを実行します。
実機ではなくSimulatorを使う場合でも、XcodeとmacOSの組み合わせが前提になります。利用可能な組み合わせは、作業開始前にAppleのXcodeシステム要件で確認し、手元のプロジェクトで使っているSchemeとTest Planを基準に決めてください。
先に決める判定項目
次の項目をメモにしてから接続すると、不要な高性能構成や複数端末構成を避けられます。
- テスト対象は単体テストだけか、UIテストまで含むか
- 実行頻度は手動、プルリクエストごと、夜間のいずれか
- 最初に固定するSimulatorの機種、OS、言語、地域、権限
- 成功条件は終了ステータスだけか、xcresultとスクリーンショットの保存まで含むか
- 失敗時に同じテストだけ再実行するか、全体を再実行するか
ローカルMacがなくてもiOSの単体テストとUIテストは実行できますか。
XcodeとSimulatorを動かせるMacへ接続できれば、コード編集を別のOSで行い、テストだけをリモート側で実行できます。ただし、UIテストは画面状態、権限ダイアログ、キーボード、アニメーションなどの影響を受けるため、最初から「接続できれば必ず安定する」と考えないでください。
SECTION 03 最初の1時間で行う固定作業
ツールチェーンの確認
リモートMacへログインしたら、まず次を確認します。<XCODE_PATH>、<PROJECT_PATH>、<SCHEME_NAME>、<DEVICE_NAME>は実際の値に置き換え、固有のプロジェクト名やユーザー名を記事や共有ログへ残さないようにします。
sw_vers
xcodebuild -version
xcode-select -p
xcodebuild -showsdks
xcrun simctl list devices available
xcode-selectが意図したXcodeを指していなければ、次のように明示します。
sudo xcode-select --switch <XCODE_PATH>
xcodebuild -version
Simulator Runtimeを追加する場合は、対応する手順をAppleのSimulator Runtime追加資料で確認します。Runtimeの名称や利用可能な組み合わせはXcodeの世代によって変わるため、記事やスクリプトへ固定値を直接埋め込まないほうが安全です。
アカウントと保存先
テスト用のmacOSアカウント、ソース用ディレクトリ、一時生成物用ディレクトリ、結果保管用ディレクトリを分けます。SSHで実行するコマンドと、ログイン済みのグラフィカルセッションで確認する作業を同じものとして扱わないでください。
mkdir -p <WORK_DIR>/source
mkdir -p <WORK_DIR>/derived-data
mkdir -p <WORK_DIR>/artifacts
mkdir -p <WORK_DIR>/logs
UIテストでグラフィカルセッションが必要かどうかは、プロジェクト、Xcode、Simulatorの組み合わせで実際に確認します。SSH経由の単体テストが成功しても、UIテストまで同じ条件で動くとは限りません。最小テストを実行し、Simulatorの起動、アプリのインストール、テストプロセスの開始までをログで確認してください。
SECTION 04 初回実行のマイルストーン
単一Schemeとdestination
最初から全テストを流さず、共有Schemeと確定したdestinationを1つ選びます。Appleのシミュレーターや実機でアプリを実行する説明を参照し、-destinationの指定が利用可能なデバイスと一致することを確認します。
xcodebuild \
-project <PROJECT_PATH>/<PROJECT_NAME>.xcodeproj \
-scheme <SCHEME_NAME> \
-destination 'platform=iOS Simulator,name=<DEVICE_NAME>,OS=<OS_VERSION>' \
-derivedDataPath <WORK_DIR>/derived-data \
test
成功・失敗を自動処理へ渡すには、コマンドの終了ステータスを確認します。標準出力だけを見て成功と判断せず、ログも保存してください。
set -o pipefail
xcodebuild \
-project <PROJECT_PATH>/<PROJECT_NAME>.xcodeproj \
-scheme <SCHEME_NAME> \
-destination 'platform=iOS Simulator,name=<DEVICE_NAME>,OS=<OS_VERSION>' \
-derivedDataPath <WORK_DIR>/derived-data \
test 2>&1 | tee <WORK_DIR>/logs/test.log
test_status=${PIPESTATUS[0]}
echo "test_status=${test_status}"
exit "${test_status}"
テスト範囲の縮小
全体が失敗したときは、アプリ本体、依存関係、テストターゲット、Simulatorのどこに問題があるか分かりません。-only-testingで対象を絞り、単体テストが起動するか、UIテストが起動するかを分離します。
xcodebuild \
-project <PROJECT_PATH>/<PROJECT_NAME>.xcodeproj \
-scheme <SCHEME_NAME> \
-destination 'platform=iOS Simulator,name=<DEVICE_NAME>,OS=<OS_VERSION>' \
-only-testing:<TEST_TARGET>/<TEST_CLASS>/<TEST_METHOD> \
test
複数の入口を増やす前に、Test Planを用途別に分けます。高速な単体テスト、通常の回帰テスト、リリース前の広いテストを同じ実行条件へ詰め込むと、失敗時の切り分けと再実行範囲が曖昧になります。Test Planの整理方法に合わせ、テスト対象と設定を明示してください。
xcodebuildでリモートMac上のXCTestを自動実行するにはどうしますか。
SSHでMacへ接続し、共有Scheme、destination、Test Planを引数で指定してxcodebuild testを実行します。最初は単一テストへ-only-testingを付け、終了ステータス、標準出力、結果パッケージの3つを回収できてから、定時実行へ進みます。Appleのコマンドラインによるビルドとテストの資料にも、コマンドライン実行の基本が示されています。
SECTION 05 UIテストの再現性
Simulator状態の固定
UIテストの失敗を、すぐサーバー性能の問題と決めつけないでください。アプリコードの不具合、待機条件の不足、前回テストのデータ、権限ダイアログ、Simulatorの残留状態、接続セッションの切断は、それぞれ別の原因です。
最低限、次の条件をTest Planや実行スクリプトで固定します。
- Simulatorの機種とOS
- 言語、地域、表示倍率、キーボード
- 通知、位置情報、カメラなどの権限
- テスト用アカウントと初期データ
- ネットワーク依存のモックまたはテスト用エンドポイント
- 前回のアプリ状態を引き継ぐか、毎回初期化するか
リセットは万能ではありません。Simulator全体の消去は複数のテストや診断材料を失う可能性があり、アプリ削除だけではキーチェーンや外部データが残ることがあります。破壊的な初期化を行う前に、ログ、スクリーンショット、失敗した結果パッケージを保存してください。
iOSシミュレーターのテストサーバーはグラフィカルセッションを常に維持する必要がありますか。
一律の答えはありません。単体テストとUIテスト、Xcodeの構成、macOSのログイン状態によって成立条件が変わるため、SSHのみの実行とログイン済みセッションでの実行を同じSchemeで比較します。UIテストが画面操作の開始前に止まるなら、サーバー性能を上げる前にセッション、権限、Simulatorの起動ログを確認してください。
SECTION 06 結果保存と失敗証拠
xcresultの回収
テスト結果は、ターミナルのログだけでなく.xcresultとして保存します。Appleのテスト結果の確認と解釈に関する資料では、結果パッケージからテスト結果や関連ログを確認する流れが説明されています。また、xcresultとxcresulttoolに関するAppleの資料も、結果を機械処理する際の基準になります。
RESULT_PATH="<WORK_DIR>/artifacts/<RUN_ID>.xcresult"
xcodebuild \
-project <PROJECT_PATH>/<PROJECT_NAME>.xcodeproj \
-scheme <SCHEME_NAME> \
-testPlan <TEST_PLAN_NAME> \
-destination 'platform=iOS Simulator,name=<DEVICE_NAME>,OS=<OS_VERSION>' \
-resultBundlePath "${RESULT_PATH}" \
-derivedDataPath <WORK_DIR>/derived-data \
test
同じパスに結果を書き込むと、前回の診断材料を上書きする危険があります。実行ごとに識別子を付け、test.log、スクリーンショット、録画、Simulatorの状態情報を同じ成果物単位で保管します。
リモート実行で生成したxcresultはどのように保存して確認しますか。
-resultBundlePathで保存先を指定し、実行終了後に結果パッケージをローカルへ転送します。XcodeのGUIで開く方法に加えて、xcresulttoolなどで自動処理する方法もありますが、結果パッケージだけで全原因を特定できるとは限りません。UIテストでは失敗時の画面画像や録画、標準出力も一緒に残してください。
scp -r <REMOTE_USER>@<REMOTE_HOST>:<WORK_DIR>/artifacts/<RUN_ID>.xcresult <LOCAL_ARTIFACT_DIR>/
失敗後の再実行入口
失敗通知には、実行ID、Scheme、Test Plan、destination、Gitコミット、終了ステータス、結果パッケージの保存先を含めます。全体を無条件に再実行するのではなく、失敗したテストだけを再実行できるスクリプトを用意してください。
Appleにはテストの繰り返し実行に関する説明があります。繰り返しで成功する場合は、テストの待機条件や共有状態の問題を疑い、繰り返しても同じ箇所で失敗する場合は、アプリやテストコードの修正へ戻ります。
SECTION 07 1週間目の運用判定
構成を増やす前の確認
単一テストが成功した後、すぐ複数Simulatorを同時実行しないでください。並列数を増やしても、依存関係の復元、ビルド、Simulatorの起動、結果保存が同時に競合すれば、処理全体が速くなるとは限りません。
次の順番で運用を確認します。
- ソースを取得する。
- 依存関係を復元する。
- 単一Schemeでテストを実行する。
- xcresultとログを保存する。
- 失敗したテストだけを再実行する。
- Macの再起動後にジョブを受け付けられるか確認する。
- DerivedData、Simulator残留物、結果パッケージの増加を点検する。
build-for-testingとtest-without-buildingを分ける構成は、ビルド成果物を再利用したい場合に検討できます。ただし、ビルド環境と実行環境の差が広がるほど、再現性の確認項目も増えます。まずは通常のtestで成功条件を固めてから分離してください。
条件分岐による選択
- 手動確認や週に数回の回帰が中心なら、必要な期間だけリモートMacを起動する構成を選びます。 常時稼働の管理や成果物整理を増やさずに済みます。
- 毎日決まった時刻にXCTestやUIテストを実行するなら、常駐テスト機を選びます。 再起動後の自動復旧、ログイン状態、ディスク使用量を先に検証します。
- 複数のOSや端末を同時に検証する必要があるなら、まずテストを直列で安定させ、リソースと失敗率を確認してから並列化します。 並列実行を前提に借りるのは避けてください。
- 物理デバイス、USB機器、特殊なネットワーク接続が必須なら、リモートMacだけで完結する前提を捨てます。 手元の実機や専用環境を併用するほうが適切です。
- 長期間、毎日安定した重い処理を続けるなら、レンタル、自前のMac、別のCI構成を総費用と保守時間で比較します。 断続的な利用だけなら、常駐費用を抱えない構成へ戻します。
方式の比較
| 運用方式 | 向いているケース | 先に確認する項目 | 避けたい判断 |
|---|---|---|---|
| 必要時だけリモートMacを利用 | 手動回帰、短期検証、低頻度のUIテスト | XcodeとRuntime、成果物の回収、再接続 | 常時稼働を前提にした自動化 |
| リモートMacを常駐化 | 夜間テスト、毎日の回帰、単一環境の継続利用 | 再起動後の復旧、ディスク整理、通知 | 失敗原因を見ずに並列化 |
| 自前Macを常設 | 物理機器、固定ネットワーク、長期の高負荷処理 | 初期費用、保守、故障時の代替 | 低頻度なのに専用機を購入 |
| 複数環境へ分散 | OSや端末条件が多いチーム | 条件ごとの再現性、成果物命名、競合 | 単一テスト未検証のまま拡張 |
SECTION 08 実行方式と最初の設定
| 判断条件 | 選ぶ方式 | 実行の基準 |
|---|---|---|
| XCTestを必要なときだけ確認する | 短期レンタル | 単一Scheme、単一destination、結果回収まで |
| UI回帰を定期実行する | 常駐リモートMac | 固定Runtime、初期化方針、失敗通知まで |
| ビルドとテストを分ける必要がある | 段階的な成果物運用 | build-for-testing後にtest-without-buildingを検証 |
| 複数Simulatorを使う | 直列確認後に限定的な並列 | 競合、ログ分離、失敗時の再現性を確認 |
| 物理デバイスが必須 | ローカル実機または専用設備を併用 | USB接続と権限を別途検証 |
最小構成が動いたら、VPSNIXのMac環境案内で利用できる接続方法を確認し、XcodeとSimulator Runtimeの組み合わせを自分のプロジェクトで照合してください。期間を決めて試す場合は、VPSNIXの料金情報を見ながら、低頻度の短期利用と常駐利用を分けて考えます。
自前のMacは物理デバイスや長期の重い処理に向きますが、購入費用、故障時の交換、OSやXcodeの保守、設置場所の確保が必要です。ローカル開発機だけで済ませる方法は手軽でも、テスト中は作業を止めることになり、夜間実行や再起動後の自動復旧には向きません。必要な期間だけMacを使い、継続的なXCTestやUIテストを別環境へ切り離したいなら、VPSNIXのリモートMacを候補に入れる価値があります。
開通前には、利用したいXcodeとSimulator Runtime、SSHや画面セッションの要件、再接続方法、結果ファイルの回収可否を確認してください。まず単一Schemeで自分のプロジェクトを通し、実行頻度が増えた段階で短期利用から常駐利用へ切り替えるのが、過剰な設備投資を避ける手順です。