Dev Tools
回文章列表·7 min

Selector 深入解析:CSS vs XPath、stable selector 原則、Playwright getByRole

寫 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 真的命中你以為的元素。

搭配工具
CSS / XPath Selector 測試器
開啟工具