テストコードが増えると、「どの組み合わせで実行すればいい?」と悩みます。
すべて実行するとCI(Continuous Integration、継続的インテグレーション)で毎回20分かかります。一部だけなら手動でチェックを切り替える必要があります。
韓国語と英語の環境でUIテストを実行したくても、よい方法が見つからないこともあります。
結論から言えば、この問題を解くためにAppleが用意したのがテストプラン(Test Plan)です。
Xcode 11で導入されたテストプランは「実行するテスト」と「実行条件」をファイルで分けて管理します。
今回は、テストプランの構造から実践的な構成、CI連携までまとめます。
テストコードにまだ慣れていなければ、まずXCTestの基礎とGiven-When-Thenで初めてのテストを書く方法をご覧ください。
テストプランとは
テストプランは拡張子.xctestplanのファイルです。内容は人が読めるJSONで、プロジェクトに追加してスキーム(Scheme)から参照します。
1つのファイルに2つの要素があります。
- 実行するテスト:含める、または除外するテストターゲット・スイート・関数。Xcode 16以降ではSwift Testingのタグも条件にできます
- 構成(Configuration):テストを実行する条件
重要なのはファイルである点です。.xctestplanをGitにコミットでき、チームで条件を共有し、変更をコードレビューできます。
チームで使うなら、プランと参照する共有スキームを一緒にバージョン管理するのが安全です。
スキーム設定だけでは不十分な理由
テストプラン以前は、実行条件がすべてスキームのTestアクションにありました。ここには構造上の限界があります。
1つのスキームにはTestアクション設定も1つだけです。「高速検証用」と「夜間全体検証用」を分けるには、スキームを複製する必要がありました。
複製するとビルド、実行、プロファイル設定まで付いてきます。テスト条件1つの変更のために、他の設定もすべてコピーすることになります。
テストプランは関係を逆転させます。1つのスキームに複数のテストプランを関連付け、各プランに複数の構成を持たせられます。
テストプランを作る
Appleの最新ガイドを基準にすると、Xcodeはスキームがビルドするテストターゲットの全テストを含む基本プランを作成します。
- Product > Scheme > Edit Test Planで基本プランを開き、保存します
- Testsタブでターゲット・タグ・スイート・関数単位に対象を選びます
- Configurationsタブで共有設定と必要な構成を追加します
プランを追加するにはProduct > Test Plan > New Test Planを使います。古いスキームではConvert to use Test Plansが表示されることがあります。これはXcode 11で導入された既存設定の変換フローです。
複数のプランを関連付けたら、Product > Test Plan > Manage Test Plansで1つをデフォルト(Default)にします。指定がないときに使われるプランです。
実行するプランはProduct > Test Planで有効にします。Command + U、つまりProduct > Testを実行すると、現在のプランが各構成で1回ずつ動きます。基本プランと有効なプランは区別しましょう。
共有設定と構成の関係
テストプランエディタにはTestsとConfigurationsの2つのタブがあります。要点はConfigurationsタブです。
このタブは上下2層で構成されます。
- Shared Settings(共有設定):全構成が継承するデフォルト値
- 個別構成:共有設定から必要な項目だけ上書きしたもの
CSSの継承に似ています。共通条件を1か所に置き、構成ごとの差分だけ指定します。
上書きした項目はエディタで太字になり、「ここだけ違う」とすぐ分かります。
設定項目は多いですが、よく使うものは次のとおりです。
| 項目 | 何を決めるか |
|---|---|
| Arguments / Environment Variables | 実行引数と環境変数 |
| Application Language / Region | アプリの言語と地域 |
| Code Coverage | カバレッジの収集有無と対象ターゲット |
| Execution Order | アルファベット順か、実行ごとにランダム化するか |
| Test Repetition Mode | テストの反復実行方式 |
| Test Timeouts | テスト1件あたりの最大実行時間 |
| Runtime Sanitization | Address·Thread·Undefined Behavior Sanitizer |
| Memory Management | Malloc Scribble, Malloc Guard Edges, Zombie Objects |
| Automatic Screen Capture | 失敗時にスクリーンショットを自動添付するか |
共通部分は共有設定に、差分は構成に。この原則でプランが増えても管理できます。
構成を分ける実践例
最もよく使うのは言語別構成です。多言語アプリでは言語によるレイアウト崩れが起きやすいですが、構成を分ければ同じUIテストを言語ごとに自動実行できます。
- 共有設定:カバレッジを有効化、失敗時にスクリーンショットを保存
- 構成A「Korean」:Application Languageを韓国語に
- 構成B「English」:Application Languageを英語に
- 構成C「RTL Pseudolanguage」:右から左へ書く言語の検証用
このプランを1回実行すると選択したテストを各構成で1回実行なり、結果レポートにも構成別に表示されます。どの言語だけで失敗したか分かります。
メモリ検査も同じです。Appleの資料によるとAddress Sanitizerはメモリを2~3倍使い、コードを2~5倍遅くする可能性があるため、実行コストを考慮する必要があります。
通常の構成ではサニタイザを無効にし、診断用構成を別にします。Address Sanitizer用とThread Sanitizer用を分け、夜間ビルドで実行できます。
カバレッジ、ランダム順、反復実行
構成で特に役立つ3つのオプションを見ていきます。
コードカバレッジは有効にするだけでなく、何を測るかを先に決めることが重要です。依存ライブラリまで1つの数値に混ぜると、アプリコードの変化を読みづらくなります。
「some targets」でアプリのターゲットだけを指定すると、意味のある数値になります。
Execution OrderではAlphabeticalとRandomを選べます。ただし、すべてのテストのデフォルトがAlphabeticalだと一般化してはいけません。
XCTestプランでRandomを選ぶと、実行ごとに順序が変わります。一方、Swift Testingはテスト関数を基本的に並行実行し、順序もランダム化しますします。2つのフレームワークは分けて考えましょう。
順序を変えて壊れるなら、前のテストが残した状態に依存している可能性が高いです。テスト間の隠れた結合を示す有用な兆候です。この独立性は優れたユニットテストのFIRST原則でも重視されます。
Test RepetitionはXcode 13で追加されたオプションで、反復方式を選べます。
| モード | 動作 | 用途 |
|---|---|---|
| Up Until Maximum Repetitions | 指定した最大回数まで結果に関係なく反復 | 間欠的な失敗の再現率を測定 |
失敗まで反復(-run-tests-until-failure) |
失敗するまで反復 | 時々壊れるテストを追跡 |
| Retry on Failure | 失敗したら指定回数まで再試行 | 不安定なUIテストのCI通過率を維持 |
Xcode 13リリースノートによると、Maximum Test Repetitionsには正の整数を指定します。公式資料で「デフォルト3回」は確認できないため、プランに保存された値を直接確認するのが正確です。
コマンドラインでは-test-iterationsで回数を指定します。-run-tests-until-failureまたは-retry-tests-on-failureと組み合わせられます。この設定がプランより優先されます。
Retry on Failureは便利ですが、慎重に使いましょう。再試行で通ると不安定なテストを残すことになります。
一時的に有効にし、原因は別途調査するのがよいでしょう。
CIでテストプランを使う
テストプランはコマンドラインでも使えます。まずxcodebuild -scheme MyApp -showTestPlansでスキームに接続されたプランを確認します。-testPlanにはファイルパスでなくプラン名を渡します。
xcodebuild test \
-project MyApp.xcodeproj \
-scheme MyApp \
-testPlan Smoke \
-destination 'platform=iOS Simulator,name=iPhone 16'
同じスキームのままプラン名だけ変え、CIジョブの実行範囲を分けられます。例えば次のようにします。
- PR(Pull Request)ごと:
-testPlan Smoke— 主要なユニットテスト中心 - メインブランチへのマージ:
-testPlan Regression— ユニット+UIテスト全体 - 夜間スケジュール:
-testPlan Nightly— サニタイザと多言語構成も含む
構成単位でさらに細かく分割できます。
Appleの最新コマンドライン例のように--only-test-configurationは指定した構成だけを実行します。一方--skip-test-configurationは指定した構成を除外して実行します。どちらもハイフンは2つです。
言語構成が5つあるプランなら、CIで5ジョブを作り、各ジョブに1構成ずつ割り当てられます。ただし並列分配はCIの仕事で、テストプランが自動でマシンを分けたり時間を短縮したりする機能ではありません。
xcodebuild test \
-scheme MyApp \
-testPlan Localization \
--only-test-configuration Korean \
-destination 'platform=iOS Simulator,name=iPhone 16'
よくある質問
Q. テストプランファイルはGitに上げますか?
A. チームで条件を共有するならコミットがおすすめです。ただしGitへのアップロードはXcodeの必須条件ではありません。プランと参照する共有スキームを一緒に管理してください。
ただしJSONなので、複数人が同時に編集すると衝突します。目的別にファイルを分けると衝突を減らせます。
Q. 構成を複数作ると時間も倍になりますか?
A. はい。構成数だけテストが繰り返されます。
多言語やサニタイザなど重い構成は通常プランから外し、夜間プランで実行するのがよいでしょう。
Q. 1つのプランにユニットテストとUIテストを入れてもよいですか?
A. 可能です。ただしユニットテストは秒単位、UIテストは分単位で、フィードバック速度が大きく異なります。
高速プランと低速プランに分けると開発フローに合います。
Q. Swift Testingで書いたテストもプランに入りますか?
A. 入ります。選択したターゲットにXCTestとSwift Testingのテストがあれば、同じプランで実行できます。Swift Testingのタグと並行実行方式も知っておくとプランを分けやすくなります。
Q. 特定のテストだけ除外したい場合は?
A. Testsタブでターゲット・スイート・関数・パラメータ化された個別ケース単位にチェックを外せます。Xcode 16以降ではSwift TestingのInclude TagsとExclude Tagsでも範囲を選べます。
コードを変更せずプランファイルだけに記録されるため、他のプランではそのまま実行されます。
テストプランはテストを書くためのツールではありません。既存テストの何を、どの条件で実行するかを決めるツールです。
スキーム複製で条件を分けていたなら、基本プランを保存し、目的別プランに整理しましょう。CIでプランと構成をジョブごとに指定すると範囲は明確になりますが、実際の短縮時間はランナー数と並列化方式次第です。
まずProduct > Scheme > Edit Test Planで基本プランを保存し、Smokeプランを1つ作ってみてください。その後の構成追加は簡単です。
出典と確認基準
- Improving code assessment by organizing tests into test plans (Apple) — 現在のXcodeのプラン作成メニュー、テスト選択単位、構成別実行、
xcodebuildオプションを確認しました。 - Testing in Xcode, WWDC19 (Apple) — Xcode 11導入時期、
.xctestplanファイル構造、既存スキームの変換フローを確認しました。 - Xcode 13 Release Notes (Apple) — 反復モード、正の整数の反復回数、コマンドラインオプションの優先順位を確認しました。
- Go further with Swift Testing, WWDC24 (Apple) — Swift Testingのデフォルト並行実行とランダムな実行順を確認しました。
- Diagnosing memory, thread, and crash issues early (Apple) — サニタイザの役割とAddress Sanitizerの実行コストを確認しました。
確認基準日は2026年8月16日です。メニュー名とコマンドライン表記は上記Apple公式資料の現在の表記を優先し、資料で確認できない反復のデフォルト回数やCI短縮効果は断定していません。

