# 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 工具完成。

