遠端團隊合併網頁變更後,最容易遺漏的往往不是 Chromium 路徑,而是 Safari 特有的表單、焦點與版面配置行為。與其在發布前臨時開啟遠端桌面逐頁點選,不如在 GPUMini 雲端 Mac 上保留一套固定的 Safari WebDriver 迴歸任務:每次部署候選版本時執行同一組關鍵流程,並在失敗時留下可供複查的頁面現場。
先固定可重複的執行邊界
Safari 自動化依賴 macOS 圖形工作階段,不能直接套用無頭瀏覽器的執行方式。先建立專用測試使用者,維持圖形工作階段的登入狀態,並確保自動任務不會與人工遠端操作同時進行。同一個使用者目錄一次只能執行一個 Safari 工作階段,避免瀏覽記錄、下載檔案與視窗狀態互相干擾。
首次準備節點時,請在互動式終端機執行驅動程式啟用與診斷:
sudo safaridriver --enable
safaridriver --diagnose
mkdir -p "$HOME/browser-tests/artifacts"
--enable 屬於節點準備作業,不要放入每次執行的自動任務。日常任務只需執行診斷;如果診斷失敗,就應停止測試,不要繼續產生一批難以判讀的逾時結果。Python 環境也應鎖定相依套件版本,將 selenium 的確切版本寫入專案相依檔案,而不是每次執行時都安裝最新版本。
瀏覽器迴歸測試的第一條基準不是「能夠啟動」,而是相同提交、相同測試資料與相同等待條件能夠得出一致結論。
使用本機夾具隔離外部波動
登入、搜尋與結帳等端對端流程可以連接測試環境,但元件互動及瀏覽器相容性檢查應儘量使用本機夾具。將表單、彈出視窗、上傳與鍵盤導覽頁面放入程式碼庫,並透過回送位址提供服務,即可排除 DNS、憑證與外部介面的波動。
set -euo pipefail
ROOT="$(cd "$(dirname "$0")" && pwd)"
ARTIFACTS="$ROOT/artifacts"
mkdir -p "$ARTIFACTS"
python3 -m http.server 8080 \
--bind 127.0.0.1 \
--directory "$ROOT/fixtures" \
>"$ARTIFACTS/http-server.log" 2>&1 &
SERVER_PID=$!
cleanup() {
kill "$SERVER_PID" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
"$ROOT/.venv/bin/python" "$ROOT/tests/safari_smoke.py"
固定連接埠之前,先使用 lsof -nP -iTCP:8080 -sTCP:LISTEN 檢查是否遭到占用。如果多個任務共用同一節點,不要隨機終止程序;應由排程層確保互斥,或為每個任務分配明確的連接埠,並將連接埠寫入測試設定。
將等待條件寫成業務狀態
最常見的不穩定寫法,是在點選後固定暫停數秒。只要機器負載略有變化,暫停時間過短就會造成誤報,過長則會拖慢整組測試。應改為等待可觀察的業務狀態,例如按鈕可以點選、結果清單出現,或狀態文字變成預期值。
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
artifacts = Path("artifacts")
driver = webdriver.Safari()
driver.set_page_load_timeout(20)
try:
driver.get("http://127.0.0.1:8080/login.html")
wait = WebDriverWait(driver, 12)
wait.until(
EC.visibility_of_element_located((By.ID, "email"))
).send_keys("tester@local.invalid")
driver.find_element(By.ID, "continue").click()
status = wait.until(
EC.visibility_of_element_located((By.ID, "result"))
)
assert status.text == "Ready"
finally:
driver.quit()
定位器應優先使用穩定的 id 或專用測試屬性,不要依賴會隨版面調整而變動的階層選擇器。斷言也必須驗證實際結果,而不只是確認元素存在:提交按鈕出現不代表提交成功,清單容器存在也不代表資料已完成呈現。
讓每次失敗都能重現現場
瀏覽器測試中代價最高的情況是「CI 顯示逾時,卻沒有現場資訊」。每個測試案例失敗時,都應立即保存螢幕截圖、頁面原始碼、目前 URL、案例名稱與時間資訊;如果等到整套測試結束後才截圖,通常只會取得已經跳轉或關閉的頁面。
| 失敗現象 | 優先保存 | 優先檢查 |
|---|---|---|
| 元素始終不可見 | 螢幕截圖、頁面原始碼 | 遮罩層、響應式版面、定位器 |
| 點選後沒有結果 | 目前 URL、按鈕狀態 | 停用狀態、焦點、事件繫結 |
| 頁面載入逾時 | URL、服務日誌 | 本機服務、重新導向、資源阻塞 |
| 驅動程式無法建立工作階段 | 驅動程式診斷 | 圖形工作階段、殘留程序、權限 |
螢幕截圖的檔名應包含測試案例識別碼與唯一執行編號,但不要寫入權杖、電子郵件地址或專案金鑰。頁面原始碼可能包含測試資料,封存前同樣必須進行去識別化處理,並設定明確的保留期限。
在異常發生時立即寫入磁碟
測試框架的失敗掛鉤應呼叫統一的證據保存函式。保存動作本身也可能失敗,因此必須分別捕捉螢幕截圖與原始碼寫入錯誤,不能讓封存異常蓋過原始斷言。
復原殘留工作階段,而不是盲目重試
任務中斷後,Safari 或驅動程式程序可能仍占用工作階段。下一次執行若直接重試,常見結果是連續無法建立工作階段。應先檢查測試使用者是否仍有執行中的任務,並確認沒有其他正常測試正在進行,再終止殘留工作階段。不要在共用節點上使用過於寬泛的程序名稱進行批次清理。
重試只適用於已知的暫時性工作階段建立失敗,而且最多只能重試一次。斷言失敗、頁面結構變更與業務狀態不符都不應自動重試,否則真實的迴歸問題會被偽裝成偶發事件。如果一次重試後恢復,應另外記錄為不穩定事件,而不是將任務標記為完全正常。
最後,將關卡拆成兩層:每次提交時執行本機夾具冒煙測試,部署候選版本時再執行少量關鍵端對端流程。上線前應確認驅動程式診斷通過、測試使用者獨占工作階段、夾具服務可供存取、明確等待中沒有固定暫停、失敗證據可以讀取,且清理函式必定執行。如此建立的就不再是一組「偶爾通過」的瀏覽器指令碼,而是一條能夠長期追蹤變化的 Safari 迴歸基準。
常見問題
Safari WebDriver 能在沒有圖形登入工作階段時執行嗎?
不建議把這種模式當作穩定基線。應使用專用測試使用者維持 macOS 圖形工作階段,並在接收工作前執行驅動程式診斷。
同一台雲端 Mac 適合平行執行多個 Safari 工作階段嗎?
先從每個使用者環境單一工作階段依序執行。需要提高處理量時,應將測試群組分散到獨立節點,而不是共用同一瀏覽器設定檔。
Safari 測試失敗時至少要保留哪些資料?
至少保留失敗畫面、頁面原始碼、目前 URL、測試名稱與驅動程式診斷結果;若需要應用程式日誌,須先移除敏感內容。
為開發與建置任務選擇一台雲端 Mac
比較兩種 M4 設定、四種租用週期與四個在售節點,再依任務需求完成部署。