修復不穩定的瀏覽器測試 (Flaky Browser Tests)

1. 不穩定情況 (The Flaky Situation)

瀏覽器測試在開發者的本地環境(如 Mac)執行順暢,但在 CI (Linux) 環境下卻經常隨機失敗,報出 Timeout 10000ms exceeded 錯誤。

2. 真正原因 (The Real Reason)

測試的不穩定(Flakiness)通常源於「非決定性的非同步操作」與環境效能差異:

  • Cloudflare Turnstile 驗證競爭: 留言按鈕預設是禁用的(顯示「驗證中」),必須等待 Turnstile 腳本執行完成並回傳 Token 後,按鈕才會被啟用。在 CI 環境中,這段 JavaScript 的執行時間可能比本地稍慢,導致測試在按鈕尚未啟用時就嘗試點擊。
  • Alpine.js 動畫過渡: 留言視窗 (Modal) 使用了 x-transition。如果測試只用了固定的 wait(1) (1秒),在 CI 伺服器負載較高(CPU Steal)時,動畫可能尚未完成,元素尚未進入可互動狀態。
  • 伺服器響應延遲 (Livewire): 提交留言是一個非同步請求。原本使用的 assertSee() 是在「那一瞬間」檢查畫面,如果伺服器回應慢了 0.1 秒,測試就會失敗。
  • 硬體效能差異: 本地開發機(如 M 系列晶片的 Mac)執行速度遠快於 CI 的虛擬機,這使得許多寫死的等待時間(Hardcoded waits)在 CI 上失效。

3. 如何修復 (How to Fix)

修復的核心原則是:將「盲目等待」改為「條件達成」

  • 提升全域容錯時間: 在 tests/Pest.php 中將瀏覽器超時從 10 秒提升至 20 秒,為 CI 負載波動提供緩衝。
    pest()->browser()->timeout(20000);
    
  • 使用 assertVisible() 代替 wait(): 確保元素在 DOM 中出現且對使用者可見後,再進行下一個動作(如填寫表單)。
  • 等待元件啟用 (assertEnabled): 針對依賴外部腳本(如 CAPTCHA)的按鈕,先進行 assertEnabled('#button-id'),確保腳本已執行完畢。
  • 改用 waitForText(): 取代 assertSee()。它會持續輪詢頁面直到文字出現為止,能完美處理 Livewire 的非同步更新。

4. 編寫瀏覽器測試的注意事項 (Best Practices)

  1. 禁止使用 wait(n):永遠不要使用寫死秒數的等待。如果需要等待,請尋找特定的 UI 變化(如元素出現、消失、內容改變)。
  2. 模擬使用者真實行為:使用者會等待按鈕變亮才點,測試也應該如此。
  3. 考慮 Headless 差異:CI 通常以 Headless 模式運行,與有介面的瀏覽器在渲染時機上可能有微小差異。
  4. 確保測試的等冪性:瀏覽器測試容易殘留資料,應確保每次測試都能清理環境或使用獨立的測試資料。

This site uses Just the Docs, a documentation theme for Jekyll.