測試程式碼累積到一定程度後,會出現新的疑問:「這些測試該用什麼組合執行?」
全部執行時,CI(Continuous Integration,持續整合)每次要花20分鐘;只執行部分測試,又得手動反覆切換勾選。
想分別在韓文與英文環境執行UI測試時,也常找不到合適的方法。
直接說結論,Apple 為了解決這個問題而提供的就是測試計畫(Test Plan)。
Xcode 11 介紹的測試計畫會將「要執行的測試」與「執行條件」分開記錄在檔案中管理。
本文一次整理測試計畫的結構、實務組態與CI整合。
如果還不熟悉撰寫測試程式碼,建議先閱讀用 XCTest 基礎與 Given-When-Then 撰寫第一個測試的方法。
什麼是測試計畫
測試計畫是副檔名為.xctestplan的檔案,內容是人類可讀的JSON。加入專案後,從Scheme參照它。
一個檔案包含兩種內容。
- 要執行的測試:決定納入或排除哪些測試目標、套件與函式。Xcode 16 以上也能使用 Swift Testing 標籤作為條件
- 組態(Configuration):決定以哪些條件執行測試
重點是它是檔案。.xctestplan可以提交至Git,讓團隊共用相同條件,也能將設定變更納入程式碼審查。
團隊使用時,建議將計畫與參照它的共用Scheme一起進行版本控制。
為什麼只靠Scheme設定不夠
測試計畫出現以前,所有執行條件都在Scheme的Test動作中,這種結構有其限制。
一個Scheme只有一組Test動作設定。因此要區分快速驗證與夜間完整驗證,就得複製Scheme。
複製Scheme時,建置、執行及設定檔設定也會一併複製。為改一個測試條件,等於連其他設定都複製。
測試計畫反轉了這個關係。一個Scheme可連結多個測試計畫,每個計畫又能包含多個組態。
建立測試計畫
依據Apple目前的說明,Xcode會建立包含Scheme所建置測試目標之所有測試的預設計畫。
- 開啟Product > Scheme > Edit Test Plan中的預設計畫並儲存
- 在Tests分頁依目標、標籤、套件或函式選擇執行對象
- 在Configurations分頁加入共用設定與需要的組態
要建立更多計畫,請使用Product > Test Plan > New Test Plan。舊Scheme可能會顯示Convert to use Test Plans,這是Xcode 11 介紹的既有設定轉換流程。
若連結了多個計畫,可在Product > Test Plan > Manage Test Plans指定一個為預設(Default)。未另行指定時便使用它。
在Product > Test Plan啟用要執行的計畫。此時執行Command + U,也就是Product > Test,會讓目前計畫的每個組態各執行一次。請區分預設計畫與目前啟用的計畫。
共用設定與組態的關係
開啟測試計畫編輯器後會看到Tests與Configurations兩個分頁,重點是Configurations。
此分頁分成上下兩層。
- Shared Settings(共用設定):所有組態繼承的預設值
- 個別組態:只覆寫共用設定中必要項目的變體
可以把它想成CSS的繼承:共用條件集中寫在一處,各組態只指定不同的項目。
被覆寫的項目會在編輯器中以粗體顯示,一眼就能看出「只有這裡不同」。
可設定的項目很多,常用的包括以下幾項。
| 項目 | 決定內容 |
|---|---|
| Arguments / Environment Variables | 執行引數與環境變數 |
| Application Language / Region | App執行時的語言與地區 |
| Code Coverage | 是否收集涵蓋率及目標Target |
| Execution Order | 依字母順序或每次執行隨機排列 |
| Test Repetition Mode | 測試重複執行方式 |
| Test Timeouts | 單一測試允許的最長執行時間 |
| Runtime Sanitization | Address·Thread·Undefined Behavior Sanitizer |
| Memory Management | Malloc Scribble, Malloc Guard Edges, Zombie Objects |
| Automatic Screen Capture | 失敗時是否自動附加螢幕截圖 |
共通設定放在共用設定,差異放在組態。遵守這個原則,計畫再多也能維持可管理性。
拆分組態的實務範例
最常見的是依語言建立組態。多語言App容易因語言不同而版面跑掉,拆分組態後即可自動以不同語言重複執行相同的UI測試。
- 共用設定:開啟涵蓋率,失敗時保留螢幕截圖
- 組態A「Korean」:將Application Language設為韓文
- 組態B「English」:將Application Language設為英文
- 組態C「RTL Pseudolanguage」:驗證由右至左書寫的語言
執行一次此計畫後會選定的測試會在每個組態執行一次,結果報告也會依組態分開顯示,能立刻看出只在哪種語言失敗。
記憶體檢查也是如此。依Apple文件,Address Sanitizer可能使用2~3倍記憶體,並讓程式碼變慢2~5倍,必須考量執行成本。
平時組態關閉Sanitizer,另設診斷組態。例如分開Address Sanitizer與Thread Sanitizer組態,在夜間建置執行。
涵蓋率、隨機順序與重複執行
接著看組態中特別實用的三個選項。
程式碼涵蓋率不只是開啟而已,重要的是先決定測量對象。若把相依函式庫也混入同一數值,會難以判讀App程式碼的變化。
選擇「some targets」只指定自己的App目標,才能得到有意義的數值。
在Execution Order可選Alphabetical或Random,但不能一概而論地說所有測試預設都是Alphabetical。
在XCTest計畫選Random後,每次執行的順序都會打亂。另一方面,Swift Testing預設會平行執行測試函式,順序也會隨機化。要分開理解兩個框架的行為。
若改變順序就失敗,很可能依賴前一個測試留下的狀態。這是揭露測試間隱藏耦合的有用訊號。這種獨立性在優良單元測試的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確認與Scheme連結的計畫。-testPlan傳入的是計畫名稱,不是檔案路徑。
xcodebuild test \
-project MyApp.xcodeproj \
-scheme MyApp \
-testPlan Smoke \
-destination 'platform=iOS Simulator,name=iPhone 16'
保留相同Scheme,只切換計畫名稱,就能分割CI工作執行範圍。例如:
- 每個PR(Pull Request):
-testPlan Smoke— 以核心單元測試為主 - 合併至主分支:
-testPlan Regression— 全部單元+UI測試 - 夜間排程:
-testPlan Nightly— 包含Sanitizer與多語言組態
也能依組態進一步拆分。
如Apple目前的命令列範例,--only-test-configuration只執行指定組態;相反地,--skip-test-configuration會排除指定組態。兩個選項都使用兩個連字號。
若計畫有5個語言組態,可在CI建立5個工作,各分配一個組態。但平行分配是CI系統的工作,不是測試計畫自動分配機器或縮短時間的功能。
xcodebuild test \
-scheme MyApp \
-testPlan Localization \
--only-test-configuration Korean \
-destination 'platform=iOS Simulator,name=iPhone 16'
常見問題
Q. 測試計畫檔案要提交到Git嗎?
A. 要共用相同執行條件,建議提交。但上傳Git並非Xcode的必要條件,請同時管理計畫與參照它的共用Scheme。
不過JSON讓多人同時修改時容易衝突。依計畫目的拆檔可降低衝突頻率。
Q. 建立多個組態,時間不也會加倍嗎?
A. 沒錯。測試會依組態數量重複執行。
因此多語言、Sanitizer等耗時組態,適合從平時計畫移出,改在夜間計畫執行。
Q. 一個計畫可以同時放單元測試與UI測試嗎?
A. 可以。但單元測試以秒計,UI測試以分鐘計,回饋速度差異很大。
拆成快速與慢速計畫更符合開發流程。
Q. 用Swift Testing撰寫的測試也能放入計畫嗎?
A. 可以。選定目標同時包含XCTest與Swift Testing測試時,可在同一計畫執行。也了解Swift Testing的標籤與平行執行方式,會更容易拆分計畫。
Q. 想排除特定測試怎麼辦?
A. 可在Tests分頁依目標、套件、函式或參數化個別案例取消勾選。Xcode 16以上也能用Swift Testing的Include Tags與Exclude Tags選擇範圍。
這只記錄在計畫檔案,不修改程式碼,因此其他計畫仍會照常執行。
測試計畫不是重新撰寫測試的工具,而是決定既有測試要執行哪些、使用什麼條件的工具。
如果一直用複製Scheme拆分條件,請先儲存預設計畫,再整理成目的不同的計畫。在CI中按工作指定計畫與組態可明確界定範圍,但實際縮短時間取決於Runner數量與平行化方式。
先用Product > Scheme > Edit Test Plan儲存預設計畫,再建立一個Smoke計畫。之後加入組態會容易許多。
來源與確認基準
- Improving code assessment by organizing tests into test plans (Apple) — 確認目前Xcode的計畫建立選單、測試選擇單位、依組態執行及
xcodebuild選項。 - Testing in Xcode, WWDC19 (Apple) — 確認Xcode 11導入時間、
.xctestplan檔案結構與既有Scheme轉換流程。 - Xcode 13 Release Notes (Apple) — 確認重複模式、正整數次數及命令列選項優先順序。
- Go further with Swift Testing, WWDC24 (Apple) — 確認Swift Testing預設平行執行與隨機執行順序。
- Diagnosing memory, thread, and crash issues early (Apple) — 確認Sanitizer的作用與Address Sanitizer的執行成本。
確認基準日為2026年8月16日。選單名稱與命令列寫法優先採用上述Apple官方文件目前的表記;文件未確認的預設重複次數或CI縮時數值不作斷言。

