Appium-關於appium的原生控件的 xpath 定位問題及常用方法
最近遇到的項目,發現很多元素,都沒有標明id、text、content-desc,classname中又有很多是相同,導致無法定位
第一,appium1.5及之後的版本廢棄了name屬性(如name=賬單,將不被支持用於定位),所以基本的定位就用下id就好了。其他的不多說了。
第二,下面就來說一下關於xpath的定位。主要場景為沒有id或者沒有text,或者text是一個不可控的值(或者叫會發生變化的值,就比如text字段為10元,可能這個10每次會變)的時候。其實簡單點就是按路徑定位包括一級或者多級路徑。順便說一下,路徑方式分兩種,一種是絕對路徑(以第一個標簽為參照物),另一種是相對路徑(已其他已知的標簽為參照物),且在定位的時候盡量采用相對路徑的方式。
1,先說說有id或者text的場景使用xpath的情況。(有id或者name為什麽不直接用?以下均為相對路徑)
上面說的name被廢棄了,但是xpath的寫法如//android.widget.TextView[@text="賬單"]是被支持的
就比如上面的"賬單"和"我要"的id都是com.wlqq:id/title_left_btn,並且假設當前頁面只有這兩個位置id為前面寫的,那麽你在用id定位"賬單"的時候,就可以用xpath了,因為id已經不唯一了。
用id定位“賬單”的為:
xpath=(//android.widget.TextView[@resource-id="com.wlqq:id/title_left_btn"])[1],
定位"我要"的為:
xpath=(//android.widget.TextView[@resource-id="com.wlqq:id/title_left_btn"])[2]
此處註意三點:
a,下標是從1開始,而不是0;
b,如果有下標,需要用括號把前面的部分括起來,並且前面需要加xpath=,可能有些人習慣了前面都加xpath=,但是像我這種只習慣寫//開頭,不寫xpath=的就被坑慘了。。。反正不容易發現是因為沒有寫xpath=,也可能是我個人比較坑吧。
c,就是和web不一樣的就是標簽的取值,在這裏取的是class的值=android.widget.TextView而不是看到的標簽TextView,具體原因沒有深究。反正記住用class代替標簽就對了。
另外,上面的只是為了說明只有1個層級的時候xpath的用法,1層也算是一種相對路徑吧。因為沒有從第一個位置的屬性開始寫。xpath的書寫規則基本是越少越好。所以層級也是越少越好。有1層可以唯一定位就不要2層。 可能有點廢話了。
2,現在就來說說沒有id或者name的場景。 先來一張圖:
現在有一個場景是,我要定位到我需要點擊上面那個小人圖標,但是沒有text、id、content-desc,唯一classname還是和其它相同,能想到的方法是用xpath方式
用絕對路徑的寫法就是:如果圖上的第一個是最頂上的話,就是:
這樣的,也就是需要7個層級,依次寫下來就是:
//android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.RelativeLayout/android.widget.LinearLayout[2]/android.widget.ImageButton
這種寫法註意一下幾點:
a , [2]註意是2而不是3,因為與標簽的值有關。只有2個LinearLayout。
b , 路徑長度偏長,而且因為只有class的值,對於一些頁面控件較多的,可能不止一個,也就是可能這種寫法也都不是唯一。
c , 絕對路徑基本很少使用,如果人品太差,遇到頁面全是沒有id或者name的,那就沒辦法了。或者考慮一些坐標。
3,(重要)沒有id或者name的場景下使用相對路徑的辦法來定位。主要介紹一下層級關系中的父子關系(上下級)和兄弟關系。
大家可以看到,這個圖裏面有一個唯一的中文詞匯--"錢包"。我們可以通過這個錢包來定位我們的小人圖片。先分析下位置關系
找找關系也就是如圖所示,小人圖標3是錢包1的弟弟2LinearLayout標簽的兒子ImageButton。兒子好理解,xpath的層級關系也就是父子關系用/表示。//android.widget.LinearLayout/android.widget.ImageButton這樣就能表示弟弟的兒子了。但是現在問題是怎麽表示錢包的弟弟?xpath裏面有一個軸,簡單點可以理解為一個函數吧。我這樣認為的。preceding-sibling:: 可以找到節點前面也就是哥哥節點,following-sibling::可以找到節點後面也就是弟弟節點,關於軸的更多用法啊,可以自行百度xpath的語法。這裏還有一個用的多的就是parent:: ,可以找到節點的父親節點。但是父親節點可以用..表示。下面就來具體說一下怎麽用:
基本知識已經介紹到此了。那麽這裏的定位方法就是上圖中的3個層級://android.widget.TextView[@text="錢包"]/following-sibling::android.widget.LinearLayout/android.widget.ImageButton。 第一級就同前面說的唯一的找到錢包這個位置,後面的一級就是錢包的弟弟,也就是following-sibling::android.widget.LinearLayout。當然註意因為是緊挨著的,所以弟弟沒有下班,可想而知如果是第幾個弟弟,就加個下標吧。哥哥也是同理。
前面用到了兄弟的關系,下面說一下兒子與父親的關系。父子關系還是用圖來說明
我們的錢包1的父親2有一個兒子3的兒子4就是我們的小人圖標。這就是找關系。關系找到了,那我們就可以用這個關系來寫xpath了。
也就是錢包(//android.widget.TextView[@text="錢包"])的父親(/parent::android.widget.RelativeLayout )的第二個class=android.widget.LinearLayout的兒子(/android.widget.LinearLayout[2])的兒子(小人/android.widget.ImageButton),好,我們連起來就是://android.widget.TextView[@text="錢包"]/parent::android.widget.RelativeLayout/android.widget.LinearLayout[2]/android.widget.ImageButton。
順便說一下父親這個位置可以用..來代替,相比很多人都知道..在路徑裏面指的就是上級。所以可以用//android.widget.TextView[@text="錢包"]/../android.widget.LinearLayout[2]/android.widget.ImageButton這個來代替上面的寫法。
註:最後再強調下,關於這個地方,下標為什麽是[2],是因為只與class相同的有關。錢包的class不一樣。所以它就不算了。
關於相對路徑的父子關系,以及兄弟關系,相比大家應該有所體會了吧。如果還是沒太懂,咱們再來個復雜點的例子。可能只是舉例說明下語法。實際下面的可能不會這樣復雜的寫。先上圖:
假設我們需要通過加入購物車這個位置來定位我們的立即定位按鈕,那麽,我們的一種寫法就是圖上的這個關系7層級。也就是加入購物車7(//android.widget.TextView[@text="加入購物車"])的父親1(/..)的父親2(/..)的父親3(/..)的第二個兄弟4(/following-sibling::android.view.View[2])的兒子5(/android.view.View)的兒子6(也就是我們的立即購買/android.widget.TextView),
連起來就是
//android.widget.TextView[@text="加入購物車"]/../../../following-sibling::android.view.View[2]/android.view.View/android.widget.TextView。
註意:使用text的時候避免使用輸入框的默認輸入值,因為當你真實輸入值之後,就沒有這個text了,也就找不到路徑了。另外也可以用模糊匹配,xpath有一個contains函數。用法//android.widget.TextView[contains(@text,"購物車")].也能找到“加入購物車”這個位置。
場景:定位請輸入密碼這個輸入框,上圖,沒有id、text、content-desc,classname也有很多重復
使用xpath,手寫定位
1、先選取登錄按鈕作為節點
//android.widget.TextView[@text=‘登錄‘]
2、再定位到父級
/.. 也就是 /parent::android.view.View
3、再定位同級的哥哥
/preceding-sibling::android.view.View[1]
註意:[1],下標是向上數的,適用於preceding-sibling
如果是following-sibling,則向下數的
都是從1開始取下標
4、再定位到孩子
child::android.widget.EditText
連起來就是:
//android.widget.TextView[@text=‘登錄‘]/../preceding-sibling::android.view.View[1]/child::android.widget.EditText
Appium-關於appium的原生控件的 xpath 定位問題及常用方法