Xcode 不穩定測試
- 已設定Xcode 測試洞察。Tuist 會從每次測試執行上傳的測試洞察事件中偵測不穩定測試。
不穩定測試是指在相同程式碼下多次執行時,會產生不同結果(通過或失敗)的測試。它們會削弱你對測試套件的信任,並浪費開發人員調查誤報失敗的時間。Tuist 會自動偵測不穩定測試,並協助你追蹤其變化。

不穩定偵測如何運作#
Tuist 透過兩種方式偵測不穩定測試:
測試重試#
當你使用重試功能執行測試時,Tuist 會分析每次嘗試的結果。如果某個測試在某些嘗試中失敗,但在其他嘗試中通過,它就會被標記為不穩定。
透過 tuist xcodebuild test 傳遞 -retry-tests-on-failure 或 -test-iterations:
tuist xcodebuild test \
-scheme MyScheme \
-retry-tests-on-failure \
-test-iterations 3

跨執行偵測#
即使沒有測試重試,Tuist 也可以透過比較同一個 commit、scheme 和報告的目標裝置(裝置名稱、平台和 OS 版本)在不同 CI 執行中的結果來偵測不穩定測試。如果一個測試在一個相符的 CI 執行中通過,但在另一個中失敗,則該失敗的執行會被標記為不穩定。無論哪個結果先到達,此機制都會生效。
同一個測試在不同 scheme 或目標裝置上的行為可能持續不同。例如,在 iOS 26 上通過而在 iOS 27 上失敗,並不構成不穩定性。來自不同 scheme、裝置、平台或 OS 版本的結果不會互相標記為不穩定。
無法歸屬到特定目標裝置的結果會與其他未歸屬的結果進行比較,這與已知目標裝置的結果分開處理。這包括缺少或不完整目標裝置中繼資料的報告,以及包含多個不同目標裝置的報告,因為個別測試結果尚無法歸屬到特定目標裝置。此群組中環境之間的差異仍可能產生錯誤的不穩定分類。基於重試的偵測仍然適用。現有的歷史不穩定分類保持不變。
這對於捕捉那些失敗頻率不足以被重試機制捕捉,但仍會導致間歇性 CI 失敗的不穩定測試特別有用。
管理不穩定測試#
自動清除#
偵測和清除是透過專案上的自動化警示執行的。每個專案都會獲得一個預設的「不穩定測試偵測」自動化,其觸發條件會將測試標記為不穩定,而其恢復動作會在測試經過設定的恢復視窗(預設14 天)且未再次觸發後清除標記。你可以在設定 → 自動化下編輯它,以更改恢復視窗、更換恢復動作(例如同時解除隔離),或完全停用恢復功能,讓測試保持標記為不穩定,直到你手動清除為止。
手動管理#
你也可以從測試案例詳情頁面手動將測試標記為不穩定或取消標記。這在以下情況很有用:
- 你想在修復已知不穩定測試的同時承認其存在
- 測試因基礎設施問題而被錯誤標記
隔離不穩定測試#
隔離會將不穩定測試孤立起來,使其在你修復期間不會阻礙 CI。預設情況下,這是一個手動操作——你可以從儀表板中的測試案例詳情頁面進行隔離、解除隔離和切換模式——但你也可以將其連接到設定 → 自動化下的自動化警示,以便當測試超過不穩定閾值時自動靜音(或跳過),並在恢復時解除隔離。每次轉換,無論是手動還是自動,都會記錄在測試案例的審核日誌中。
被隔離的測試處於以下兩種模式之一:
- 靜音:測試仍然執行,但
tuist xcodebuild test會掩蓋失敗。失敗仍然會提供給不穩定測試偵測器,因此你可以繼續監控測試而不破壞建構。選擇此模式用於你正在積極調查的測試。 - 跳過:xcodebuild 會收到
-skip-testing <identifier>,因此測試永遠不會啟動。它不會產生新結果,並會從不穩定測試儀表板中消失,直到你重新啟用它。當測試損壞、緩慢或不穩定性持續到執行它只是浪費 CI 分鐘數時,請選擇此模式。
為何被隔離的測試可能顯示為通過#
- 靜音測試仍然執行。失敗的靜音測試會在測試案例執行中記錄為失敗並標記為不穩定——單一測試的狀態不會被改寫。被覆蓋的是整體測試執行:如果執行中每個失敗的測試案例都被靜音,則整個執行會報告為通過,因此靜音失敗不會破壞 CI。
- 跳過的測試根本不會執行,因此儀表板會繼續顯示測試最後一次實際執行的狀態——該快照可能是幾週前的。
執行測試#
tuist xcodebuild test是一個自動尊重這兩種模式的直通包裝器。像呼叫 xcodebuild 一樣使用它:
tuist xcodebuild test -scheme MyScheme
跳過的測試會作為-skip-testing <identifier>附加到你的 xcodebuild 呼叫中,並且永遠不會啟動。靜音測試正常執行;如果它們失敗,失敗會在產生的建構狀態中被掩蓋。
繞過隔離#
tuist xcodebuild test接受--skip-quarantine以執行所有內容,包括靜音和跳過的測試:
tuist xcodebuild test --skip-quarantine -scheme MyScheme
直接呼叫 xcodebuild#
如果你無法透過tuist xcodebuild test執行,請使用tuist test case list自行將隔離的測試擴展為-skip-testing參數:
xcodebuild test \
-scheme MyScheme \
$(tuist test case list --quarantined --skip-testing)
這是tuist xcodebuild test之外的安全預設值:靜音測試的失敗掩蓋僅在你透過該命令執行時才會發生,因此跳過這兩種模式可以避免虛假的 CI 失敗。如果你需要更細粒度的控制,請改用tuist xcodebuild test。
壓力測試新測試#
壓力測試閘道會重新執行你分支新增的測試案例,每個案例在全新程序中執行多次,並回報結果為不穩定的案例。你可以在引入該問題的拉取請求上立即看到新的不穩定測試,而不是幾週後才發現。
如果測試案例在過去 90 天內未在專案的預設分支或已合併至該分支的提交中於 CI 執行過,則視為新案例。使用合併佇列時,佇列執行的測試案例會在佇列提交合併後被識別,若佇列將其移除,則仍保持為新案例。提交是透過 Tuist 在 CI 上收集的Git 歷史記錄進行比對。
啟用功能#
閘道預設為關閉狀態。請設定模式:
report:警告每個不穩定的測試案例。執行仍會根據其自身結果結束。enforce:不穩定的測試案例會導致執行失敗,退出碼與測試失敗相同。
先使用report幾週,以了解enforce會攔截哪些情況。
將它放在直通參數之前傳遞,或設定TUIST_TEST_STRESS_NEW_TESTS以針對每個 CI 管道變更模式:
tuist xcodebuild test --stress-new-tests report -scheme MyScheme
每個測試重新執行的次數#
取決於測試案例在首次執行所需的時間:
| 首次執行 | 重新執行次數 |
|---|---|
| 最多 5 秒 | 10 |
| 最多 10 秒 | 5 |
| 最多 30 秒 | 3 |
| 最多 5 分鐘 | 2 |
| 超過 5 分鐘 | 不重新執行 |
重新執行會重用首次執行所建構的內容。執行會在達到 200 個測試案例或 10 分鐘時停止(以先到者為準),並指出觸發了哪個限制。
閘道不執行的情況#
- 首次執行已經失敗。請先修復這些測試。
- 專案沒有預設分支,或者尚未在其上執行任何 CI。
- 超過 30% 的專案測試案例看起來是新的。重新執行這麼多案例會得不償失。在剛開始向 Tuist 回報的專案,或在一次重新命名移動了大量測試之後,可能會出現這種情況。
Slack 通知#
透過在 Slack 整合中設定不穩定測試警示,在測試變得不穩定時立即收到通知。
查詢不穩定和隔離狀態#
CLI#
tuist test case命令樹顯示 Tuist 追蹤測試案例的所有資訊:
tuist test case list --flaky # only flaky test cases
tuist test case list --quarantined # only muted or skipped test cases
tuist test case show <test_case_id> # detail: flakiness rate, last status, run counts
tuist test case events <test_case_id> # audit log: marked_flaky, muted, skipped, ...
tuist test case run list <test_case_id> # run history with status and duration
tuist test case run show <test_case_run_id> # single run, including failure breakdown
所有這些命令都接受--json以便進行腳本編寫。
REST API#
相同的數據可透過 HTTP 取得——請參閱 API 參考中的測試案例端點以獲取完整的路由、過濾器和回應欄位列表。若要變更測試案例的狀態(標記或取消標記為不穩定、靜音、跳過),請發送帶有is_flaky或state的PATCH /api/projects/{account_handle}/{project_handle}/tests/test-cases/{test_case_id}。相同的變更也可透過tuist test case update以及update_test_case Model Context Protocol 工具進行。