元件定位策略:accessibility id 優先、少用 XPath
#Appium #元件定位 #flaky #QA 實戰
測試寫完第一天跑得很順,第二天開發改了一個按鈕的層級,XPath 爛掉三個 test。問題出在定位策略。 我專案裡的 locator 越積越多。每次 App 改版,維護工時大部分花在這裡。 定位優先序 從最穩到最脆,我框架裡的硬規則是: 1. accessibility id :跨平台、語意化,開發標好就很穩 2. 平台原生的查詢方式 :Android 用 android uiautomator 走 resource id,iOS 用 class chain 或 predicate 3. XPath :前兩者都不行才用,而且禁止整頁掃描式的寫法 4. 圖片比對 :最後的退路,解析度或視覺一改就失效 選定位方式的原則只有一個:選最不依賴 UI 結構和文案的那個。 為什麼少用 XPath XPath 在行動裝置上有兩個致命傷: 慢 和 脆 。 慢是因為 Appium 要遍歷整棵 UI tree 才能比對路徑,一個複雜畫面可能有幾百個節點。脆是因為絕對路徑和 UI 結構綁死,改個層級就斷。 我的 locator 長什麼樣 盤點專案時,我把 locator 依類型分開,看每一類在改版時有多容易斷: | 類型 | 改版時的風險 | | | | | accessibility id | 低,最穩 | | android uiautomator(resource id) | 低,id 很少改 | | resource id | 低 | | iOS class chain | 中 | | XPath | 高 ,層級一動就斷 | | 圖片 | 高 ,換皮或解析度不同就斷 | | 純文字 | 最高 ,文案改版或多語系就斷 | 文字、圖片、XPath 這三類加起來占了一大塊。照原則,這些都不該存在。它們會在這裡,是因為原則有一個前提: App 得先給你穩定的 id 。 原則做不到的時候 我推過開發在元件上加 accessibility id。成本不高:Android 加 contentDescription,iOS 加 .accessibilityIdentifier("login btn"),Compose 加 Modifier.testTag。但這些工作排不進開發的時程,進度很慢。 最極端的是一個核心頁面:整頁自己繪製,dump 出來只有一個全螢幕節點,文字、描述、resource id 全是空的。我為這頁寫了一份提案給主管,把問題講成開發聽得懂的話:這不是「這頁測不了」,是「漏加了 id」。那個元件本來就能加上無障礙標記,補上去就能讓自動化看見。 等不到的部分,我只能先在腳本這邊繞: 有文字的,用文字定位 沒文字的,用圖片比對 圖片也不行的,用 OCR 或座標 這些都能用,但每一條都是債。那一大塊高風險 locator,就是這樣累積出來的。 回頭看,我覺得對單人維護來說,唯一值得長期投資的方向就是 accessibility id 覆蓋率。自己修 XPath 修再多次,下一版還是會斷;開發標一次,之後就一直穩。所以就算推得慢,我還是會每一季回頭看一次覆蓋率有沒有進步。 已經有的債,怎麼管 債已經在那裡了,我的做法是 不回頭改舊的,只擋新增的 : 不主動改舊 locator。 能跑的就先讓它跑,等那一頁本來就要改的時候再順手換掉。一次全面翻修,一個人做不完,做到一半反而兩套並存。 新增的 locator 一律照優先序。 違反的在 review 時直接改。 程式碼裡的 XPath 凍結在現有數量。 盤點時還有不少 XPath 直接寫在 .py 裡,沒有收進設定檔。規則是不准再增加,新的一律寫進設定檔。 新增前先查重。 我寫了一支小腳本,用文字搜尋整個專案的 locator,避免同一個元件被不同人(或不同次的自己)寫了兩份。 Android/iOS 分開管理 同一個語意元件,Android 和 iOS 的 id 幾乎一定不一樣。最簡單的做法是用 dict 依平台選: 我實際的專案是一頁一份 yml,兩個平台寫在同一個檔案裡: 放在 yml 的好處是 UI 改版時只動設定檔,不動程式。如果改版時發現非改 .py 不可,就代表那個元件的定位沒有好好外置,順手搬出去。 有一個我自己踩過的坑:yml 裡的 value 要寫「原料」,不要寫「成品」。我的框架讀到 type: resourceid 時,會自動幫你包成 new UiSelector().resourceId(...)。如果你在 value 裡自己先包好,就會被包兩層,元件怎麼樣都找不到,錯誤訊息還看不出原因。規則要寫清楚:哪種 type 會自動加工、哪種原樣使用,不然每個新加入的人(包括 AI)都會踩一次。