1. 程式人生 > >Appium+python自動化(十一)- 元素定位祕籍助你打通任督二脈 - 下卷(超詳解)

Appium+python自動化(十一)- 元素定位祕籍助你打通任督二脈 - 下卷(超詳解)

簡介

  巨集哥看你骨骼驚奇,印堂發亮,必是練武之奇才! 按照上一篇的節目預告,這一篇還是繼續由巨集哥給小夥伴們分享元素定位,是不是按照上一篇的祕籍修煉,是不是感覺到頭頂蓋好像被掀開,內氣從頭上冒出去,頓時覺得整個身體都融化了,而且身輕如燕啊!而且控制不住手,想要動手操作一番呢?那還在等什麼呢,和巨集哥一起練起來吧!!!

1、 List定位

  List故名思義就是一個列表,在python裡面也有list這一個說法,如果你不是很理解什麼是list,這裡暫且理解為一個數組或者說一個集合。首先一個list是一個集合,那麼他的個數也就成了不確定性,所以這裡需要用複數,所以在我們定位時我們不能夠接著用find_element_by_id等等定位方式了,我們需要用他的複數形式find_elements_by_id,所有的定位方式都一樣需要採用複數加s。這裡我們接著上篇的案例講,如何使用list定位想定位的元素。首先看一下圖片:

 

 

我們檢視圖片可以知道我們能夠很輕鬆的通過id定位到整個祖父節點,我們接下來需要做的事定位這個祖父節點下所有的“android.widget.RelativeLayout”父節點,同樣的首先我們看一張圖:

 

 

這裡我們需要直接使用定位複數的方法來操作,直接看程式碼(祖父節點定位到父節點):

element= driver.find_element_by_id("com.taobao.taobao:id/rv_main_container")
elements = element.find_elements_by_class_name("android.widget.FrameLayout")

通過上面的程式碼我們直接定位了com.taobao.taobao:id/rv_main_container父節點下的所有android.widget.FrameLayout子節點,但是由於這個android.widget.FrameLayout子節點下邊還有許多相同的android.widget.LinearLayout孫節點。

 

這裡我們需要直接使用定位複數的方法來操作,直接看程式碼(父節點定位到孫節點):

1 elements = element.find_elements_by_class_name("android.widget.FrameLayout")
2 elements1 = elements[1].find_elements_by_class_name("android.widget.LinearLayout")

現在我們需要怎麼去操作這個子節點了,這裡有兩種方法:

1、前面我們講了List你可以理解為一個數組或者一個集合,這裡定位的所有子節點最後就成了個list,如果我們要訪問這個list裡面的某一個元素我們可以像訪問陣列中的資料一樣通過下標訪問。最後的程式碼就是下面這個樣子:

1 element= driver.find_element_by_id("com.taobao.taobao:id/rv_main_container")
2 elements = element.find_elements_by_class_name("android.widget.FrameLayout")
3 elements1 = elements[1].find_elements_by_class_name("android.widget.LinearLayout")
4 elements1[1].click()

上面的程式碼最後的結果是選擇了“聚划算”這個標籤頁面,然後點選進入。

 

備註:如果初學者不理解是如何通過下標訪問的,這裡說一下下標是從0開始,如果要訪問list i中的第一個元素結果就是i[0],這部分知識可以看一下python基礎。

2、如果你要訪問List裡面的元素,那麼我們是否可以通過for迴圈語句來依次訪問呢?這個在自動化中會經常用到。下面你可以通過這個思路自己去實戰一下,看能否達到預期效果。下面看我的程式碼:

1 element= driver.find_element_by_id("com.taobao.taobao:id/rv_main_container")
2 elements = element.find_elements_by_class_name("android.widget.FrameLayout")
3 elements1 = elements[1].find_elements_by_class_name("android.widget.LinearLayout")
4 for ele in elements1:
5     ele.click()

看上面的程式碼,我們通過迴圈去訪問這個list裡面的每一個元素,因為每次迴圈得到的都是其中一個元素,那麼我們只需要在這個元素上加上你想要的操作即可,所以我們這裡可以直接點選進去。

如果你動手做到這裡會發現一個問題,你進入到第一個標籤後沒一會兒系統就會報錯,為什麼呢?你也可以試著去解決這個問題,後面我們會講解這塊兒知識。

2、 內嵌H5定位

2.1 hybrid定位思考

在web自動化中我們會遇見frame的問題,在遇見這些內嵌的標籤後我們需要做的就是切換視窗,那麼在app自動化測試也有類似的情況就是我們經常看見的內嵌html,在我們原生的app中增加一個由html做成的頁面。大家可以思考一下這種情況怎麼操作。

2.2 hybrid常見定位問題分析

首先我們看一下下面一張圖片:

通過右邊的結構圖我們能夠清晰的看見整個頁面就是一個webview,無論從什麼角度來定位我們都不能夠很好的進行,如果這個時候我們需要操作頁面的元素就需要通過切換contexts來完成。但是在講這個知識點之前大家先按照網上的知識來試一下處理這個頁面,看能否成功。下面先說大家會遇見的問題:

1、可能你看到有的文章顯示我們不需要通過切換contexts就能夠完成定位,這樣的情況有,但是那種情況作者只在微博登入、qq登入等第三方登入時遇見過,如果不是這樣的情況而像上面的情況就沒辦法通過類似的方法進行完成,所以我希望讀者遇見這種情況時自己動手去操作,看什麼方式更加適合自己的專案。

2、需要切換contexts那麼就需要獲取頁面的所有contexts,此時你通過官網或者其他文章的知識通過下面的方法來獲取,可能會報錯,這種情況關係不大。

1 webview = self.driver.contexts
2 print webview

如果你通過上面的程式碼來除錯但是卻報錯,但是其他資料卻沒問題時你也不要著急,這裡你需要確定兩件事情:(1)、app打包的時候需要開啟webview 的debug屬性setWebContentDebuggingEnabled(true),這個直接讓開發加上就好。一般情況是開啟的,畢竟他們也要除錯。(2)、你用很多手機去除錯,發現有一些可以有一些不可以,但是你用模擬器卻都可以,根據官方給出的答案是這個時候你需要去將手機root,然後再試。目前作者遇見了這兩種情況,第二種我也是除錯了很久才找到原因。

2.3 hybrid定位講解

這兩個問題解決後那麼定位webview就輕鬆搞定,直接看程式碼:

1 webview = driver.contexts
2 driver.switch_to.context(webview[1])
3 driver.find_element_by_link_text('PHP').click()

對於初學者對於上面的程式碼可能不是很理解,下面我們看一下日誌:

大家這裡不用管我執行程式碼和之前的區別(多了一個self),我們看下面控制檯的輸出,輸出的是一個list,前面說過list和陣列類似,在這個list裡面有兩個元素“NATIVE_APP”,“WEBVIEW_cn_com_open_mooc”,第一個元素是我們原生的app的contexts,後面的則是我們的webview的context,所以我們需要獲取webview的context時只需要通過這個list的下表來進行訪問。

我們獲取到webview的context後只需要通過driver.switch_to.context()進行切換就好。當切換後我們就可以像定位web一樣進行定位。

看下面一張圖片我們通過瀏覽器將h5頁面開啟:

通過上面的圖片我們就能夠很輕鬆的像web一樣進行定位,也就可以使用web的一些定位方式。看到這裡是不是覺得解決了一個難題呢?動手去吧。

2.4 hybrid問題實戰

通過前面的學習我相信你已經有了一些實戰能力,這裡給大家提一個問題,我們獲取到的contexts每次一定是兩個嗎?如果不是兩個那麼我們上面的指令碼是不是就沒辦法用了呢?可以思考一下這裡怎麼解決,在看我們下面的思路以及解決方案。

無論在什麼頁面我們去獲取contexts時他無論有幾個但是他的型別是不是肯定都是一個list呢?既然是list那麼我們是否可以取到裡面的每一個值,然後把每一個值進行判斷,只要找出我們的webview就可以了呢?下面看程式碼:

1 #獲取當前頁面所有的contexts
2 webview = driver.contexts
3 #在獲取到的contexts list裡面去挨個迴圈
4 for context in webview:
5   #判斷迴圈中單個的context是否是webview,如果是就進行切換,並且跳出迴圈
6   if 'WEBVIEW' in context:
7     driver.switch_to.context(context)
8     break
9 driver.find_element_by_link_text('PHP').click()

通過上面的程式碼我們是否完美的解決了內嵌H5的定位問題呢?動手吧

3、 滑動定位

3.1 滑動定位方式

在app自動化中我們經常會遇見一個問題,我們需要查詢的元素不在當前可展示的螢幕,至於在什麼地方我們不知道,如果這個時候我們一直使用在當前頁面查詢,那麼系統就會報錯,為了解決這個問題我們就需要使用滑動查詢。

首先的思路是我們在需要查詢物件的頁面查詢一下該元素,判斷該元素是否在當前頁面,如果該元素不在該頁面那麼我們就需要去互動螢幕,到我們的下一螢幕,然後再進行查詢,依次類推到找到為止。

3.2 滑動定位思路分析

方式我們有了,那麼我們就需要知道實現這個功能應該有哪些點。下面跟著我一起來分析一下:

1、需要查詢的元素我們是不是需要知道是什麼呢?這個需要先確定

2、我們需要找的頁面是在我們的當前頁面的上方還是下方還是左方還是右方,我們不能確定,那麼我們是否需要確定我們需要滑動的方向?

3、元素和方向有了,但是你知道我們每次需要滑動螢幕的多少嗎?那麼我們是否需要先去獲取螢幕的大小,然後針對不同的方向去計算一個滑動的值呢?

萬事具備只欠東風,去按照這個思路動手練習一吧。

3.3 滑動定位實戰

一、根據上面的思路我們能首先來確定我們需要查詢的元素,看下面圖片:

我們要找實戰推薦後面的“換一換”按鈕,然後進行點選。首先我們檢視他的定位資訊

最後我們查詢元素的定位資訊程式碼如下:

1 self.driver.find_element_by_id('cn.com.open.mooc:id/tv_replace').click()

 

  對於有一定基礎的人可能會覺得這個很low,但是有沒有思考過一個問題,我們可以通過這個程式碼去執行,在沒有這按鈕的時候卻會報錯,也就沒有辦法執行下去了,那麼需要怎麼處理呢?所以這個時候我們需要有一些python的容錯知識,即使我們的程式碼執行出錯了,那麼也要讓他按照我們的意思執行下去。try.......except.......,這個就是我們python中的容錯處理 ,下面我們看新增後的程式碼:

1 try:
2 self.driver.find_element_by_id('cn.com.open.mooc:id/tv_replace').click()
3 except Exception,e:
4 print e

try的意思就是告訴編譯器試著去執行他下面這一段程式碼,如果報錯了,那麼你就把except裡面的錯誤資訊打印出來。

二、有了元素現在我們需要知道的是不是就是該怎麼滑動介面了呢?首先我們看一下下面這張圖片:

在我們使用app的過程中存在上面幾種滑動情況,我們把整個介面看作為一個座標系(x,y),如果我們需要往上滑動,那麼我們是不是就是x軸不動,y軸從下往上動呢?往下就是x軸不動,y軸從上往下呢?同理左右滑動是不是就是應該y軸不動x軸左右滑動呢?可以好好去體會一下,腦海中有個畫面。

在appium中滑動我們所需要使用的方法就是swipe函式,至於往哪個方向滑動就是看我們裡面的x,y的值,如果我們需要下往上滑動那麼我們就應該是:

1 self.driver.swipe(x1,y1,x1,y2,t)

上面的程式碼x軸的值不變,y軸的值進行了變化,所以是沿著上下進行滑動的,從y2滑動到了y1點。t代表的是多少時間完成這個動作,或者說這個時間持續多久。

備註:這裡需要注意的是螢幕的x,y的值是從左上角開始取的,左上角為(0,0),右下角是最大。

三、上面滑動的方法看著是好用,但是我們不可能每次都去填寫一個座標,那樣太low,所以我們需要獲取螢幕大小,直接看程式碼:

1 x = self.driver.get_window_size()['width']
2 y = self.driver.get_window_size()['height']

  上面的程式碼就是我們獲取到的x,y軸。通過思路我們的程式碼都有了,下面我們要做的就是對原來的程式碼進行修改,進行一個封裝。下面看程式碼,這個暫時看不懂沒關係,到後面我們學了python'基礎就能夠看懂了。先思路,然後瞭解。

 1 #獲取螢幕大小
 2          
 3 def getSize(self):
 4   x = self.driver.get_window_size()['width']
 5   y = self.driver.get_window_size()['height']
 6   return (x,y)
 7          
 8          
 9 #向左滑動
10 def swipeLeft(self,t):
11   l=self.getSize()
12   x1=int(l[0]*0.9)
13   y1=int(l[1]*0.5)
14   x2=int(l[0]*0.1)
15   self.driver.swipe(x1,y1,x2,y1,t)
16                
17 #向右滑動
18 def swipeRight(self,t):
19   l=self.getSize()
20   x1=int(l[0]*0.25)
21   y1=int(l[1]*0.5)
22   x2=int(l[0]*0.75)
23   self.driver.swipe(x1,y1,x2,y1,t)
24                
25 #向上滑動
26 def swipeUp(self,t):
27   l=self.getSize()
28   x1=int(l[0]*0.5)
29   y1=int(l[1]*0.8)
30   y2=int(l[1]*0.4)
31   self.driver.swipe(x1,y1,x1,y2,t)
32   time.sleep(5)
33          
34 #向下滑動
35 def swipeDown(self,t):
36   l=self.getSize()
37   x1=int(l[0]*0.5)
38   y1=int(l[1]*0.25)
39   y2=int(l[1]*0.75)
40   self.driver.swipe(x1,y1,x1,y2,t)
41          
42 #查詢元素,沒找到滑動
43 def findLocal(self):
44   x = 1
45   while x==1:
46     if self.fact() == 1:
47       self.swipeUp(2000)
48       time.sleep(3)
49       self.fact()
50     else:
51       print "找到了"
52       x=2
53              
54          
55          
56 #遞迴
57 def fact(self):
58   n =1
59   try:
60     self.driver.find_element_by_id('cn.com.open.mooc:id/tv_replace').click()
61   except Exception,e:
62     return n

  通過檢視上面程式碼的整個邏輯就是1、首先去查詢元素,如果找到了我就直接點選。2、如果沒有找到元素那麼我就往上滑動(這裡可以自己選擇),滑動後再次進行查詢,如果找到就點選,沒有找到繼續滑動。動手動手,這裡知識點很重要!雖然後面會有一些替代方法,但是思路、演算法很重要。

4、小結

   好了,元素定位,常見的大致就這些,這個目前就分享到這裡吧,以後如果遇到,巨集哥給小夥伴再補上!!!

 

支援巨集哥的朋友們和巨集哥的巨集粉記得點波推薦哦,您的肯定就是我進步的動力。巨集哥先在這裡給您道謝了,謝您嘞~~

個人公眾號

微信群