寫 E2E 自動化測試最痛的不是寫 step,是 selector 三天兩頭就壞。這篇整理我看過、寫過、修過的所有 selector 套路,以及 Playwright 為什麼推 role-based locator。
CSS vs XPath:什麼時候用哪個
CSS 適合:
- 結構單純、用 class / id / attribute 就能定位
- 速度比 XPath 快 30~50%(每個瀏覽器原生引擎都最佳化過 CSS)
- 可讀性更好,團隊熟悉度高
XPath 適合:
- 要用 text content 定位:
//button[text()="提交"](CSS 直到:has()才有類似能力,Safari 16+ 才支援) - 要回溯父元素:
//input/../label(CSS 完全做不到) - 複雜結構條件:「找有 class A 但兒子沒有 class B 的 div」
我的選法:能用 CSS 就用 CSS。XPath 留給「定位 text 內容」「往上爬 parent」這兩種專屬場景。
Stable selector 三原則(寫一次穩半年)
1. 永遠優先 data-testid:跟產品分離,UI 改版不會壞。Cypress / Playwright / Selenium 都認。
<button data-testid="submit-payment">立刻付款</button>
選擇器:[data-testid="submit-payment"] — UI 改成 icon button 都還是這個。
2. 絕對避免 nth-child / nth-of-type:列表項順序一變就壞,後端排序改一下就完蛋。
3. class name 用「語意 class」不用「樣式 class」:.btn-primary-rounded-lg-hover 這種 Tailwind 拼出來的 class 隨時會改;改用 .submit-button 這種 semantic class。或乾脆走 data-testid。
4. ID 也不要太信:很多 framework 會生 id="react-aria-:r1:" 這種 hydration 後變動的 ID。可以用就用,不行就 fallback。
Playwright 的 getByRole 為什麼是 game changer
傳統 selector 邏輯:「找 DOM 結構某個位置」 — 結構變就壞。
Playwright 的 getByRole 改成:「找 accessibility tree 上 role=button 且 accessible name 含『提交』的元素」。
page.getByRole('button', { name: '提交' })
優點:
- 跟 visual 重組無關:UI 把按鈕從右邊搬到左邊,測試還是過
- 跟 a11y 同步:測試壞 = a11y 壞,順便發現可用性問題
- 跨框架:不管你是 React / Vue / Angular,role 都一致
隱性好處:逼你把 <div onClick> 改成 <button>,a11y 自動升等。
動態渲染的時序問題
最常見錯誤:waitForTimeout(2000) — 死等 2 秒。網慢就 flake,網快就慢。
正確做法:
- Playwright:
await expect(page.getByText('成功')).toBeVisible()— 內建 auto-wait,預設 5 秒輪詢 - Selenium:
WebDriverWait(driver, 10).until(EC.visibility_of_element_located(...)) - Robot Framework:
Wait Until Element Is Visible selector timeout=10s
進階:等網路請求完
await page.waitForResponse(resp =>
resp.url().includes('/api/payment') && resp.status() === 200
);
await expect(page.getByText('付款成功')).toBeVisible();
這比等元素出現更精準 — 元素可能因為快取秒出,但實際 API 還沒回。
跨瀏覽器一致性陷阱
:has()selector:Safari 16+、Chrome 105+、Firefox 121+ 才支援。寫了你以為穩,Firefox 100 就掛text=Playwright pseudo-locator:這是 Playwright 自家語法,直接貼到瀏覽器 console 不會 work/text()="..."XPath:Safari 對某些 XPath 函數實作不完整,真的要跨瀏覽器測就先用 Selector tester 工具 在不同瀏覽器跑一次- shadow DOM:預設 CSS selector 穿不進 shadow DOM,Playwright 的 locator 會自動穿,但你貼到 DevTools 不會
寫測試前先到 Selector 測試工具 用 HTML 片段試:CSS / XPath 切換,確認 selector 真的命中你以為的元素。