堅持原創輸出,點選藍字關注我吧
作者:清菡
部落格:oschina、雲+社群、知乎等各大平臺都有。
文章總覽圖
一、往期回顧
loc='new UiSelector().text("全程班")'
WebDriverWait(driver,20).until(EC.visibility_of_element_located((MobileBy.ANDROID_UIAUTOMATOR,loc))
driver.find_element_by_android_uiautomator(loc).click()
這個步驟後進入了這個頁面:
進入這個頁面也是需要時間的。WebView 這個元素當中,放的才是 html 頁面。真的等到 html 頁面載入出來之後,再去獲取所有相關的內容,這樣比較好。
萬一切過來的時候,html 頁面還沒有開始載入,我就馬上去獲取當前所有可以操作的物件,這樣很容易丟失,所以也一樣要講究等待。
講究等待,首先等到 WebView 這個元素出現。等到 WebView 這個 class 控制元件出現,class 值代表它的控制元件。
# 等待Web View元素出現 -Web View裡面放的是Html
WebDriverWait(driver,20).until(EC.visibility_of_element_located((MobileBy.CLASS_NAME,'android.webkit.WebView')))
sleep
1 秒鐘,確保裡面的 html,所有的都能載入完成。
time.sleep(1)
二、怎麼切換?
用什麼樣的語句來獲取我們的 WebView、獲取我們的原生控制元件呢?
它這個東西在我們 App 當中叫做context
,翻譯成中文就是上下文。
上下文在我們自動化中就是指可以切換的東西,就是我們的原生控制元件。 原生控制元件是我們預設的,就像視窗切換就是我們預設的視窗是一樣的。原生控制元件是它的預設上下文。開啟 app,預設就是在它的原生控制元件當中。
WebView 就是它的第二種context
。
只要當前頁面中有 WebView,它就會顯示出來,有 2 個就會顯示 2 個。如圖片中這個例子中只有一個 Webview,所以它只顯示一個 WebView。
這就是context
上下文。
只有這種情況下需要切換,其它情況下都是原生控制元件就不需要切換,不用管它,一旦有 html 頁面就需要考慮這些事情了。
三、上下文切換
可用的上下文(Contexts)
列出所有可用的上下文(contexts)
driver.contexts
driver.window_handles
獲取所有視窗的 handle,返回 list 列表。
當前上下文(context):列出當前的上下文(context)
driver.current_context
切換至預設的上下文(context)
切換回預設的上下文(context)。(譯者注:一般就是原生上下文 “NATIVE_APP”)
driver.switch_to.context(None)
當前 Activity:獲取當前的 Acticity。僅支援 Android。
driver.current_activity
當前包名(package):獲取當前包名(package)。僅支援 Android 。
driver.current_package
上下文的操作方式在這裡,和 Windows 視窗是一模一樣的。和 Web 自動化中所謂的視窗是一樣的。
首先列出所有可用的上下文。就像列出目前所有開啟的視窗是一樣的。
這個上下文,有 WebView 的時候,也是在執行程式碼的時候,它進入了有 WebView 的頁面當中,才會有多個,沒有進入有 WebView 的頁面當中只有一個 WebView 的(相當於一個大箱子,箱子開啟後有多個)。
列出所有可用的上下文,再去切換至需要的上下文。怎麼切換呢?他們得到的結果也是個列表啊。
列表當中放的值呢,不是原生控制元件就是 WebView。所以它也有下標。如果要切換的話就是driver.switch_to.context(None)
None 表示什麼呢?
表示切換回預設的上下文,按照 Web 自動化的講法就是預設的視窗,在我們這裡就是預設的原生控制元件裡面。
如果你想切換到 WebView 的話,driver.contexts
返回值 0,列表取下標 1,2,3,4 都是可以取得。也可以將你得到的 Web 名稱放在driver.switch_to.context(None)
中替換 None 就可以了。
driver.switch_to.context(None)
可以切進去,也可以切出來。如果你想獲取當前的視窗,當前的上下文,叫做driver.current_context
。
它的做法與視窗是一模一樣的。Web 自動化中叫做視窗,這裡叫做上下文。 其它的時候不需要切換,但是有視窗需要交替的時候就必須要切換。有 iframe,需要更換 html 頁面的時候就需要切換,其它情況下就不切換。
現在在這個地方已經等到了這個所有的 WebView 出現了,所以接下來這樣做:
button[@class="bottom-btn buy"]
相當於 App 自動化和 Web 自動化組合起來用了,無縫切換,不需要改什麼,照著套路用就好了:
# 切換之後:當前的操作物件:html頁面。
# 等待元素可見
# 因為是通用的,所以接下來的程式碼是web自動化的程式碼
WebDriverWait(driver,20).until(EC.visibility_of_element_located((MobileBy.XPATH,'//button[@class="bottom-btn buy"]')))
# 這個用Mobileby或者By都無所謂。
driver.find_element_by_xpath('//button[@class="bottom-btn buy"]').click()
列出了當前的上下文:
['NATIVE_APP', 'WEBVIEW_com.tencent.mobileqq:mini', 'WEBVIEW_com.保密']
NATIVE_APP 是當前的原生控制元件,按照 web 自動化來說,是預設的主視窗。
是因為這段程式碼:
# 1、先列出所有的context
cons=driver.contexts #列表
#也是按照出現的先後順序,WebView是操作過程中才出現的,所以它肯定排隊。
print(cons)
一定要開啟 webview debug 屬性,如果你沒有開啟它,那麼這 2 項,在這裡獲取的時候是看不到的:
就只有一個了,就是 NATIVE_APP。只能看到 NATIVE_APP 是切換不到 WebView 的。一定要保證能夠識別得到,才能夠去切換。
技巧: 報錯的時候先看第一行程式碼,看看在你自己當前指令碼當中到底是哪一行出錯了。
四、樣例程式碼
手機設定中開啟著顯示佈局邊界的情況下,然後 run 程式碼。因為 App 介面有變更,所以程式碼和現有介面不一致,即立即購買現在成了報名截止並跳轉至 QQ 介面。程式碼提示找不到元素請不要奇怪,因為為了便於理解,放的元素還是立即購買的元素。
此程式碼只是樣例,不一定保證在你的電腦上就能執行成功,請根據實際情況修改。
from appium import webdriver
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from appium.webdriver.common.mobileby import MobileBy
desired_caps={}
# 平臺型別
desired_caps["platformName"]="Android"
# 平臺版本號
desired_caps["platformVersion"]="10"
# 裝置名稱
desired_caps["deviceName"]="2NSDU20410017297"
# app 包名
desired_caps["appPackage"]="輸入appPackage"
# app 入口 acitivity
desired_caps["appActivity"]="輸入appActivity"
# 連線Appium server。前提:appium desktop要啟動。有監聽埠。
# 將desired_caps傳送給appium server。開啟app
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)
loc='new UiSelector().text("全程班")'
WebDriverWait(driver,20).until(EC.visibility_of_element_located((MobileBy.ANDROID_UIAUTOMATOR,loc)))
driver.find_element_by_android_uiautomator(loc).click()
# 等待Web View元素出現 -Web View裡面放的是Html
WebDriverWait(driver,20).until(EC.visibility_of_element_located((MobileBy.CLASS_NAME,'android.webkit.WebView')))
# 因為只是等它這個元素出現了,至於裡面的html有沒有載入完成,並不是很確定。
time.sleep(1)#為了穩定起見,稍微sleep 1秒,確保裡面的Html,所有的都能載入完成。
# 前提:可以識別到WebView,
# 這個識別不是肉眼識別,而是通過呼叫程式碼的時候可以識別。需要開啟app的webview debug除錯屬性,對外可見。
# context #原生控制元件 #webview
# 1、先列出所有的context
cons=driver.contexts #列表
#也是按照出現的先後順序,WebView是操作過程中才出現的,所以它肯定排隊。
print(cons)
# 2、切換至WebView,要確保chromedriver的版本要與webView的版本匹配。也要放置在對應的位置。
driver.switch_to.context(cons[-1])#這個地方沒有給你提示,不代表你錯了,照著操作就好了。
# 先寫個-1,因為現在不知道WebView的名字。但是知道WebView一定是出現在最後的就可以了。
# 3、切換之後:當前的操作物件:html頁面。
# 等待元素可見
# 因為是通用的,所以接下來的程式碼是web自動化的程式碼
WebDriverWait(driver,20).until(EC.visibility_of_element_located((MobileBy.XPATH,'//button[@class="bottom-btn buy"]')))
# 這個用Mobileby或者By都無所謂。
driver.find_element_by_xpath('//button[@class="bottom-btn buy"]').click()
# 這裡為什麼用MobileBy.XPATH而不是By.XPATH?
# MobileBy這個類繼承了By,所以這個用Mobileby或者By都無所謂。
# 原理:大家背後走的都是同一套邏輯,同一段請求,同一種命令。都是find_element
# 只不過我們查詢元素的方式不一樣。所以它只是一個外部的形式而已,在內部也是一樣的。
# 即便用Selenium Webdriver 寫程式碼,也可以把MobileBy引進來,只要我不去用移動端的定位方式,都是可以做的。
遇到的問題
appium 報錯 session not created: This version of ChromeDriver only supports Chrome version 84
靠譜連結: https://www.codenong.com/jsb8d9e8746809/
溫馨提示: 如果你的程式碼沒問題,還報錯,那麼就換 Appium 版本吧,Appium 的 bug 很多。
上篇文章 中此處程式碼錯了,應該改成這樣:
公眾號 清菡軟體測試 首發,更多原創文章:清菡軟體測試 95+原創文章,歡迎關注、交流,禁止第三方擅自轉載。