端到端測試
端到端測試會執行真實的應用程式,以使用者取得的方式建置並啟動,並從外部驅動它:瀏覽器點擊頁面、Shell 執行命令列、或客戶端呼叫 API。它能捕捉單元測試無法發現的問題:元件間的連接、配置、路由,以及各部分之間的縫隙。它也是最慢且最脆弱的一種測試,因此每個測試都必須證明其存在價值,並以每次執行都給出相同答案的方式撰寫。
相關技能 unit-testing 涵蓋單一單元的測試。當某個行為可以在兩個層級檢查時,應使用單元測試進行檢查。
先遵守專案規則
使用儲存庫的 e2e 框架、設定、資料夾、測試固件和腳本。在撰寫測試前,請先閱讀現有的測試和設定檔(playwright.config.*、cypress.config.* 等),並與它們保持一致。AGENTS.md、CONTRIBUTING.md 和套件腳本會說明如何執行 e2e 測試以及針對什麼環境執行。當它們與本技能衝突時,以它們為準。請勿新增第二個 e2e 框架。
預設工具
當儲存庫還沒有任何 e2e 工具時,對於任何涉及瀏覽器的測試,請使用 Playwright(@playwright/test)。它適用於任何 Web 應用程式,無論後端使用何種語言。此處的範例使用 Playwright,但這些規則適用於任何工具。
哪些情況值得撰寫 e2e 測試
- 關鍵旅程: 註冊、登入、產品存在的核心任務、結帳,以及任何先前在生產環境中出錯的功能。
- 整合點: 需要真實後端、驗證或路由才能運作的頁面。
- 每個旅程一個快樂路徑, 加上使用者實際會遇到的失敗情況(例如密碼錯誤、付款被拒絕)。
將所有其他情況推到單元測試:每個驗證規則、每個格式化案例、每個計算的分支。試圖涵蓋所有案例的 e2e 測試套件會變得過於緩慢而無法執行,且過於不穩定而無法信任。
定位器
像使用者一樣尋找元素,以便測試能夠適應標記和樣式的變更。
- 依序偏好: 角色和無障礙名稱(
getByRole('button', { name: 'Save' }))、標籤(getByLabel('Email'))、可見文字,最後是當沒有任何使用者可見的元素穩定時,才使用測試 ID(getByTestId)。 - 避免: CSS 類別、XPath、
nth-child和元素位置。它們會在版面變更時失效,且無法說明使用者所見的內容。 - 如果無法透過角色或標籤找到元素,這通常是一個無障礙錯誤。應說明此情況,而不是使用脆弱的選擇器。
等待
絕不固定等待一段時間。sleep、waitForTimeout 和 setTimeout 在慢速的 CI 機器上會顯得太短,在快速的機器上則浪費時間。
- 使用會重試直到通過或逾時的斷言:
await expect(page.getByRole('alert')).toHaveText('已儲存')。 - 等待使用者會等待的條件:元素可見、URL 改變、收到回應(
page.waitForResponse)、檔案寫入。 - 不要為了修復失敗而提高逾時時間。如果某個步驟確實需要較長時間,請為該步驟提高逾時時間並說明原因。
- 不要強制執行框架檢查之外的操作(
{ force: true })。如果點擊需要強制執行,表示有東西遮蓋了元素或元素尚未就緒,而真實使用者也會遇到同樣的問題。
測試資料與隔離
- 每個測試設定自己的狀態,並且可以單獨執行、以任何順序執行,以及與其他測試並行執行。沒有任何測試依賴於另一個測試先執行。
- 為每個測試產生唯一的資料(例如產生的電子郵件或 ID),以避免並行工作者和重試時發生衝突。
- 透過後門設定資料。 透過 API、種子腳本或測試固件建立使用者和記錄,而不是透過點擊 UI。只有正在測試的旅程才透過 UI 執行。
- 登入一次並重複使用。 在設定步驟中儲存已驗證的狀態(Playwright 的
storageState),而不是在每個測試中透過表單登入。 - 僅偽造你不擁有的內容。 在網路邊界使用
page.route()或本地端偽造服務來模擬第三方服務(付款、電子郵件、地圖)。保持自己的前端、後端和資料庫是真實的,否則這就不是端到端測試。
命令列與 API 端到端測試
相同的規則適用,但沒有瀏覽器。使用專案的單元測試執行器(JavaScript 和 TypeScript 使用 Vitest)來執行命令列測試,並使用單獨的設定或專案,以免拖慢單元測試套件。API 測試可以使用 Playwright 的 request 固件或相同的執行器。
- 執行已建置的成品,使用者執行它的方式,而不是透過僅供測試用的進入點來執行原始碼。
- 為每個測試提供一個全新的暫存目錄作為其工作目錄,並將
HOME、設定路徑和快取指向該目錄,以避免測試讀取或修改開發者的真實檔案。 - 分別斷言退出碼、標準輸出、標準錯誤和建立的檔案。僅檢查「它沒有拋出錯誤」的測試會遺漏錯誤的輸出。
- 在比較輸出之前,先移除或凍結每次執行之間會變化的內容(時間戳記、暫存路徑、持續時間)。
當測試失敗時
在更改任何內容之前,先閱讀證據。
- 取得成品。 為失敗開啟追蹤記錄、螢幕截圖和影片(Playwright:
trace: 'on-first-retry'、screenshot: 'only-on-failure'),並從 CI 執行中開啟追蹤記錄(npx playwright show-trace <trace.zip>)。追蹤記錄會顯示每個步驟的頁面、網路和主控台內容。 - 判斷是誰的錯誤。 如果應用程式有問題,修復應用程式;測試已經盡了它的職責。如果測試有問題,修復測試。如果環境有問題(服務關閉、連接埠被佔用),修復設定。
- 重現問題 使用 CI 所用的相同命令、瀏覽器和設定,包括無頭模式和工作者數量。
- 對於不穩定的測試,找出競賽條件。 常見原因:固定等待、在頁面準備好之前進行操作、與其他測試共享資料,或被動畫或提示覆蓋的元素。修復該原因,然後在同一行中多次執行測試(
npx playwright test <file> --repeat-each=20 --retries=0),然後才能宣告修復完成。
CI 重試可以保持管線正常,但僅在重試時才通過的測試仍然是失敗的。將報告中的「不穩定」視為需要修復的錯誤,而不是一種狀態。未經使用者同意,絕不跳過、刪除或隔離測試以達到綠燈,也絕不放鬆斷言以使其通過。
檢查清單
在提交 e2e 測試或審查它們之前:
- 該旅程需要一個 e2e 測試。單元測試可以檢查的案例屬於單元測試。
- 定位器使用角色、標籤、文字或測試 ID。沒有 CSS 鏈或 XPath。
- 沒有固定等待、強制操作或提高的整體逾時時間。
- 每個測試建立自己的唯一資料,並且可以單獨執行和並行執行。
- 僅模擬第三方服務。
- 失敗時會在 CI 中留下追蹤記錄或螢幕截圖。
- 新的或修復的測試在關閉重試的情況下連續通過多次。