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