Selenium:xPath 定位實踐

fiskeryang發表於2020-08-22

使用過 selenium 的朋友相信都瞭解 selenium 給使用者提供了幾種不同的元素定位方式。
今天在這裡我們不討論幾種定位方式的優劣,只針對性的討論 xpath 的使用方法與一些技巧。本人一直是堅定的 xpath 黨,定位方式非常靈活,而且運用熟練了之後,還可以對 UI 自動化的 PO 模式進行擴充套件。

絕對路徑 vs 相對路徑
相對其他定位方式來說,xpath 的使用有一定的門檻,剛開始接觸時可能只會透過瀏覽器的定位元素功能直接複製 xpath 絕對路徑,比如這樣的

/*[@id="root"]/section/div[1]/div/div/div/form/section[1]/div[2]/ul/li[1]/button

這樣的絕對路徑 xpath 至少存在兩個方面的問題
1、可讀性和可維護性很差,這一長串路徑看得人眼睛都花,別說維護了。
2、健壯性差,現在的前端框架很多元素都是根據實際業務動態生成的,而頁面元素結構稍微變化,繼續使用絕對路徑就可能會定位不到元素了。
所以說,想要使用 xpath 進行定位,使用絕對路徑是肯定不行的。而是用相對路徑就要求我們必須對 xpath 有一定的瞭解,xpath 的幾個關鍵知識離不開以下兩個方面
1、xpath 提供的各種函式
2、xpath 軸
我們再來分析一下上面這個 button 元素
首先它是在一個 id 為 root 的 div 容器裡中一個 form 表單的子元素
button 的子元素是一個 innerText 為【查 詢】的 span
我們可以把上面的 xpath 改造一下

//div[@id='root']//span[text()='查 詢']/..

然後按 F12 開啟 Chrome 瀏覽器的控制檯,切換到 elements 標籤,按 Ctrl+F 啟用查詢對話方塊,將 xpath 輸入到搜尋框中,可以看到瀏覽器定位到了這個 button 元素
這樣我們就透過 xpath 的相對定位定位到了這個 button 元素,並且忽略掉了 DOM 樹中大部分中間層級

可以看到透過使用 xpath 的相對路徑和過濾功能使 xpath 表示式簡潔了很多,並且因為忽略了中間層級,對 DOM 樹結構的穩定性依賴降低了很多。

Xpath 使用介紹
由於 XML/HTML 文件都是樹形結構,文件中的每一個元素物件都是樹形結構中的一個節點,xpath 就是幫助我們來對樹形結構中的節點進行精確定位的語言
那麼,我們在做 UI 自動化時需要掌握哪些與 xpath 定位相關的知識點呢?

1、路徑表示式
【.】表示當前節點,xpath 以 . 開頭的話 即表示這是根節點
【..】表示當前節點的父節點
【/】表示選取當前節點的下一級節點
【//】表示選取當前節點的所有下級節點
【@】表示選取節點屬性
【text()】表示選取節點文字
2、謂語
謂語被括在方括號中,可以視為查詢元素的過濾條件,常見的有
使用節點屬性過濾 如 .//div[@title='xpath 定位']
使用節點索引過濾 如 .//div[1] 索引下標從 1 開始
使用節點文字過濾 如 .// div[text()='xpath 定位']
3、運算子
[+] 加 [-] 減 [] 乘 [div] 除
[=] 等於 [!=] 不等於 [<] 小於 [ >] 大於
[and] 與 [or] 或
[|] 多節點選取
**4、函式
*
xpath 支援的函式非常多,我們在這裡只討論 UI 自動化測試中常用到的函式,
另外由於主流瀏覽器現在都不支援 xpath2.0,所以我們的討論範圍僅限於 xpath1.0

contains(str1,str2) 如果 str1 包含 str2,則返回 true,否則返回 false
substring(str,start,len) 返回從 start 位置開始的指定長度的子字串。第一個字元的下標是 1
string-length(str) 返回指定字串 str 的長度
normalize-space(str) 刪除指定字串的開頭和結尾的空白,並把內部的所有空白序列替換為一個,然後返回結果
starts-with(str1,str2) 如果 str1 以 str2 開始,則返回 true,否則返回 false
ends-with(str1,str2) 如果 str1 以 str2 結尾,則返回 true,否則返回 false
not(arg) 返回引數 arg 的相反的布林值
5、軸 (列舉幾個常用的)
ancestor 選取當前節點的所有先輩(父、祖父等)。
preceding-sibling 選取當前節點之前的所有同級節點。
following-sibling 選取當前節點之後的所有同級節點。
軸使用語法為 : 軸名稱::元素 xpath,
比如 .//div[@id='1']//following-sibling::input[@name]
表示選取 id 為 1 的 div 元素之後同級的所有帶有 name 屬性的 input 元素

例項 : 用 Chrome 開啟獵聘的首頁 https://www.liepin.com
首先 ,嘗試定位首頁的搜尋輸入框元素

按 F12 開啟 Chrome 開發工具,檢視該元素屬性

切換到 Console,輸入 $x(".//input[contains(@placeholder,'輸入職位關鍵詞')]")

可以看到 定位到了兩個 input 元素,把滑鼠移動到第一個 input 元素上,搜尋欄元素啟用,第二個是一個相同的 input 的元素但是當前是隱藏狀態
然後,我們吧 xpath 改一下,指定取第一個匹配到的元素,這樣就可以精確匹配到這個搜尋輸入框了

如果不希望使用寫死索引的方式定位,我們可以再分析下這兩個 input 有什麼區別

上面分別是兩個 input 及其容器元素的結構,可以看到第二個 input 的容器 div 元素多了個 style 屬性,data-selector 也不同,那麼我們可以再改造一下 xpath
.//div[@data-selector='search-box' and not(contains(@style,'none'))]//input[contains(@placeholder,'輸入職位關鍵詞')]

這樣 ,我們就定位到了搜尋輸入框元素 ,這裡的 xpath 定位我們使用到了 contains 函式、@ 屬性定位、索引定位、and 與操作 、not 函式

接下來再看看下搜尋欄下面的行業分類索引連結

假如我們希望能夠透過分頁文字內容【IT·網際網路】來定位到這個 a 元素
xpath : .//p[@data-selector='subsite-btn']//b[text()='IT·網際網路']/..

這裡,我們使用了【..】來選取父元素,直接使用【..】來定位父元素比較簡單,但是不能加任何過濾條件,
如果還想要對父元素或更上層的元素進行過濾選擇的話需要用 xpath 軸 ancestor

我們可以看到,xpath 的相對定位是非常靈活並且健壯性較強的一種定位方式,在實際工作中也是使用的非常多的,非常推薦大家使用。

相關文章