為 MetricKit 診斷資料建立固定樣本迴歸測試

為 MetricKit 診斷資料建立固定樣本迴歸測試

MetricKit 的診斷載荷不會配合提交節奏送達。即使今天只改了解析器的一行程式碼,真正的當機或卡頓報告也可能要幾天後才會揭露欄位遺失。更穩妥的做法,是將已取得的載荷去識別化、正規化並保存為固定樣本,讓雲端 Mac 在每次提交時都重播同一批輸入。如此驗證的是診斷處理鏈路本身,而不是等待下一次偶發事件。

先劃清測試邊界

固定樣本適合涵蓋四層邏輯:原始 JSON 能否讀取、系統欄位能否映射到內部模型、敏感內容是否已清除,以及異常輸入能否降級處理。它無法證明系統一定會產生載荷,也不能取代裝置端的回呼驗證。

建議將接收程式碼與業務處理拆開。接收層只負責取得 jsonRepresentation()、寫入受保護目錄並排入上傳佇列;解析層接收 Data,輸出不依賴 MetricKit 型別的內部結構。單元測試只呼叫後者,避免測試目標必須偽造系統物件。

樣本的價值不在於模擬一次成功解析,而是固定輸入契約,讓每次修改解析器時都能回答:「哪些欄位改變了,哪些資訊遭到捨棄?」

建立可納入版本控制的正規化樣本

原始載荷可能包含套件識別碼、裝置資訊、時間、呼叫堆疊符號與本機路徑。不要直接提交。應先在受限環境中保留原始資料,再產生可放入儲存庫的正規化副本:將時間改為固定值、識別碼改為測試值、路徑替換為 $APP$HOME,並保留呼叫堆疊位址的格式,但不保留真實位址。

可以在內部格式中加入 schema,但不要改寫原始 MetricKit 版本。目錄依診斷類型組織:

Tests/Fixtures/MetricKit/
├── crash/basic.json
├── crash/missing-stack.json
├── hang/main-thread.json
├── disk-write/threshold.json
└── malformed/truncated.json

每個樣本只表達一項條件。若同一個檔案同時包含當機、卡頓與磁碟異常,測試失敗後將很難定位原因。樣本名稱應描述輸入,而不是預期結果;預期值則放在測試程式碼中,審查時更容易發現斷言是否被順手修改。

執行測試前先設置結構門禁

在編譯測試前先用 jq 進行低成本檢查,可以迅速攔截無效 JSON、遺漏版本與尚未去識別化的路徑。以下的 diagnostics 是團隊自行定義的正規化陣列,並非假設系統原始載荷具有相同結構。

set -euo pipefail

root="Tests/Fixtures/MetricKit"

find "$root" -name '*.json' -print0 |
while IFS= read -r -d '' file; do
  jq -e '
    type == "object" and
    .schema == 1 and
    (.diagnostics | type == "array") and
    all(.diagnostics[];
      (.kind | type == "string") and
      (.timestamp | type == "string") and
      (.stackID | type == "string")
    )
  ' "$file" >/dev/null

  if grep -E '/Users/|/private/var/|[A-F0-9]{16,}' "$file"; then
    echo "fixture contains unnormalized data: $file" >&2
    exit 1
  fi
done

結構門禁不應規定所有系統欄位,否則新增選用欄位時會造成無意義的失敗。只需檢查內部處理真正依賴的鍵,並讓解碼器忽略未知欄位。

用正向與反向樣本涵蓋解析契約

至少準備一組正常輸入與三組失敗輸入。測試重點不只是「沒有拋出錯誤」,而是輸出是否仍可供彙整、告警與問題排查使用。

樣本 預期行為 不應發生
完整當機資料 輸出類型、時間與堆疊識別碼 保存原始本機路徑
空診斷陣列 傳回空結果 視為解碼失敗
缺少呼叫堆疊 標記為不完整 將偽造的空堆疊視為正常資料
截斷 JSON 傳回可分類的錯誤 程序直接結束
未知類型 記錄未知列舉值 捨棄整批載荷

斷言內部模型,而非整段 JSON

整段快照很容易受到欄位順序與無關中繼資料變動的干擾。應優先斷言診斷數量、類型、穩定識別碼與去識別化結果;只有在正規化輸出需要跨系統交換時,才加入格式化 JSON 快照。錯誤也應定義為可比較的列舉值,例如 invalidJSONmissingRequiredFieldunsupportedDiagnostic,不要只比較容易變動的自然語言訊息。

整合至雲端 Mac CI

在 VMRunner 的雲端 Mac 上,應將 fixture 檢查安排在單元測試之前,並確保非互動式工作使用固定的工作目錄。一套可行的執行順序是:簽出程式碼、執行結構門禁、執行解析器單元測試、產生測試結果,最後檢查工作區是否出現尚未提交的樣本變更。

樣本變更必須單獨審查。新增系統欄位時,先確認解析器是否需要使用;若需要,應升級內部 schema,並同時提交遷移測試;若不需要,則維持寬鬆解碼。不要讓指令碼在 CI 中自動覆寫基準檔案,否則真正的欄位遺失可能會被新的錯誤結果「核准」。

合併前檢查項目

完成這些約束後,MetricKit 診斷處理就會從「收到資料後再嘗試」轉變為一般且可重複執行的工程測試。系統載荷仍需在裝置端驗證,但解析、去識別化與相容性不再依賴偶然送達的報告。

常見問題

MetricKit 固定樣本能完全取代實機驗證嗎?

不能。固定樣本適合驗證解析、去識別化、映射及異常降級邏輯;系統是否正常產生並回傳診斷載荷,仍須透過受控裝置測試與正式環境觀測確認。

應該直接把原始 MetricKit JSON 放進版本庫嗎?

不建議。可將去識別化的原始資料存放在限制存取的位置,版本庫只保留移除使用者識別碼、本機路徑與敏感內容後的正規化樣本。

出現未知的新欄位時,CI 應立即失敗嗎?

通常不需要。解碼器應容許新增的選用欄位,但若內部契約要求的診斷類型、時間或呼叫堆疊識別碼缺失,就必須讓測試失敗。

獨享實體節點

將下一次建置放到獨享雲端 Mac 上執行

選擇機型、節點與計費週期。下單前會完整列出設定與美元金額,實際可用狀態以控制台即時回傳為準。

選擇方案並下單