# Bazel 不穩定測試 {#bazel-flaky-tests}

> [!IMPORTANT]
> **必要條件**
>
> - 透過 `tuist bazel setup` 設定 <.localized_link href="/guides/features/test-insights/bazel">Bazel 測試洞察</.localized_link>。

不穩定測試係指同一份代碼多次執行時會產生唔同結果（通過或失敗）嘅測試。呢啲測試會削弱你對測試套件嘅信心，亦會浪費開發人員排查虛假失敗嘅時間。Tuist 會自動偵測不穩定測試，並幫你長期追蹤佢哋。


## 不穩定偵測運作方式 {#how-it-works}

Tuist 會使用 Bazel `test.xml` 報告中的個別測試案例，以偵測在重試之間，以及在持續整合中相同提交與目標選擇的多個執行之間的不一致結果。本機執行仍然可見，但不會自動將測試案例標記為不穩定。

### 測試重試 {#test-retries}

重試為選用項目，且與隔離無關。`--flaky_test_attempts=3` 可啟用每個測試最多 **三次 Bazel 總嘗試次數**，而非額外三次重試。請將現有的重試設定保留在 `.bazelrc`，或明確傳入：

```sh
tuist bazel test -- --flaky_test_attempts=3 //app:tests
```

必須實際執行重試，Tuist 才能觀察其結果。僅憑 Bazel 目標的不穩定摘要，無法識別哪個個別案例不穩定。

### 跨執行偵測 {#cross-run-detection}

比較執行需要提交與分支的中繼資料。當你的建構環境尚未發布這些資料時，請提供：

```sh
tuist bazel test -- \
  --build_metadata=CI=true \
  --build_metadata=GIT_COMMIT="$GIT_COMMIT" \
  --build_metadata=GIT_BRANCH="$GIT_BRANCH" //app:tests
```

案例會依據目標標籤、類別名稱（若無則退回使用套件名稱）以及測試名稱來識別，詳情請參閱 <.localized_link href="/guides/features/test-insights/bazel">Bazel 測試洞察</.localized_link>。

## 管理不穩定測試 {#managing-flaky-tests}

### 自動清除 {#automatic-clearing}

偵測同清除會透過項目上嘅 **自動化警報** 執行。每個項目都會有一個預設嘅「不穩定測試偵測」自動化，佢嘅 *觸發條件* 會將測試標記為不穩定，而 *恢復機制* 則喺測試喺設定嘅恢復期（預設 **14 日**）內冇再觸發之後，就會清除呢個標記。你可以喺 **設定 → 自動化** 入面編輯佢，去更改恢復期、更改恢復動作（例如，同時將測試從隔離中釋放），或者完全停用恢復功能，咁樣測試就會一直保持標記為不穩定，直到你手動清除佢哋。

### 手動管理 {#manual-management}

你亦都可以喺測試案例詳情頁面手動標記或取消標記測試為不穩定。以下情況會有用：
- 你想喺修復問題期間確認已知嘅不穩定測試
- 由於基礎設施問題導致測試被錯誤標記


## 隔離不穩定測試 {#quarantining}

隔離會將不穩定測試案例獨立處理，令佢喺你修復期間唔會阻塞 CI。預設係**手動操作**：你可以從儀表板嘅測試案例詳情頁面隔離測試案例、解除隔離同切換模式。你亦可以將佢接駁到**設定 → 自動化**底下嘅**自動化警示**，當測試案例跨越不穩定閾值時自動靜音（或跳過），並於恢復時解除隔離。每次轉換，無論手動定自動化，都會記錄喺測試案例嘅審核日誌中。

預設情況下，恢復會等待觸發條件不再成立，所以測試案例會保持隔離狀態，直到舊有不穩定執行離開觸發視窗。如需更快恢復，請將**恢復時機**設為**無新不穩定執行**：一旦恢復視窗（以日數或執行次數計）內沒有新不穩定執行，測試案例就會恢復；每次出現新不穩定執行都會重新開始等待；恢復後，只有新不穩定執行會再次將其隔離。


`tuist bazel test` 會在每次呼叫前取得隔離政策，並將其套用至底層的 `bazel test` 執行。一般的 `bazel test` 仍會回報測試結果，但不會套用隔離：

```sh
tuist bazel test -- //app:tests
```

`--` 之後的引數會轉發至 `bazel test`。使用 `--path` 選取專案與工作目錄，並使用 `--bazel` 選取其他可執行檔（例如 `bazelisk`）。命名的 Bazel 設定（例如 `--config=ci`）為選用項目，運作方式相同。

### 靜音 {#mute}

使用 **靜音** 作為主要隔離工作流程。靜音的案例會繼續執行，其失敗仍然可見，而健康的同層案例仍會影響結果。只有當報告將每個失敗目標中的所有失敗歸因於靜音案例時，Tuist 才會將 Bazel 的測試失敗結束狀態改為成功。建構錯誤、逾時、中斷的執行、遺失或不完整的報告，以及不在靜音集合內的失敗，都會保留 Bazel 的失敗狀態。

### 略過 {#skip}

略過以 Bazel 目標為單位運作：略過一個案例會排除其整個目標（包括健康的案例），因為 Bazel 無法針對每個測試框架過濾個別案例。除非你打算排除整個目標，否則請優先使用 **靜音**。指令會列印每個被排除的目標標籤，讓建構記錄顯示隔離政策排除了哪些項目。

如果被略過的目標已不存在，Tuist 會在警告中指明該目標，並在沒有該排除的情況下執行。將過時的案例恢復為 **啟用**，以永久移除其政策。如果沒有剩餘的測試目標，Bazel 會維持其正常的「找不到測試」結束狀態。

### 點解隔離測試會顯示為通過 {#quarantined-passing}

- **靜音測試** 仍然會執行。如果靜音測試失敗，該次測試案例執行會記錄為 **失敗** 並標記為不穩定；單一測試的狀態不會被改寫。被覆蓋的是 **整體測試執行**：如果執行中所有失敗的測試案例都已被靜音，則整個執行會被報告為通過，因此靜音的失敗不會導致 CI 中斷。
- **跳過測試** 完全不會運行，因此儀表板會繼續顯示該測試上次實際執行的狀態。該快照可能已是幾週前的數據。


### 再次執行已隔離的測試 {#running-quarantined-tests-again}

若要調查或驗證修復，請繞過隔離政策：

```sh
tuist bazel test --no-quarantine -- --nocache_test_results //app:tests
```

將案例恢復為 **啟用**，以便在後續的一般執行中包含其目標。被略過的案例在明確包含之前不會產生新的執行，因此基於成功新執行的自動化需要在能夠恢復它們之前先進行驗證執行。

### 限制 {#limitations}

- 明確的 `--build_event_json_file` 或 `--target_pattern_file` 無法與作用中的隔離同時使用。請直接傳遞目標模式。
- 如果 Tuist 無法取得隔離政策，指令會在執行測試前停止。它永遠不會套用部分政策。當你刻意想要在不使用隔離的情況下執行時，請使用 `--no-quarantine`。
- 與較舊的套件型身份下記錄的靜音相符的失敗仍會阻擋執行，指令會指出要重新套用的政策。

如需了解底層行為，請參閱 Bazel 的 [測試過濾器](https://bazel.build/docs/user-manual#test-filter)、[指令選項](https://bazel.build/reference/command-line-reference) 及 [建構事件描述](https://bazel.build/remote/bep-glossary)。

## Slack 通知 {#slack-notifications}

喺你嘅 Slack 整合中設定 <.localized_link href="/guides/integrations/slack#flaky-test-alerts">不穩定測試警報</.localized_link>，以便喺測試變得不穩定時收到通知。


## 查詢不穩定及隔離狀態 {#querying}

### CLI {#querying-cli}

`tuist test case` 命令樹公開 Tuist 追蹤測試案例的所有資料：

```sh
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 {#querying-rest-api}

同樣的資料可透過 HTTP 取得。請參閱 API 參考文件中的 [測試案例端點](https://tuist.dev/api/docs#tag/test-cases)，以查看完整的路由、篩選條件及回應欄位清單。如要變更測試案例的狀態（標記或取消標記為不穩定、靜音、跳過），請傳送 `PATCH /api/projects/{account_handle}/{project_handle}/tests/test-cases/{test_case_id}` 並附上 `is_flaky` 或 `state`。相同的變更亦可透過 `tuist test case update` 及 `update_test_case` MCP 工具進行。

