Appium上下文和H5測試(二)

清菡發表於2020-11-30

堅持原創輸出,點選藍字關注我吧

作者:清菡
部落格: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')))

sleep1 秒鐘,確保裡面的 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+原創文章,歡迎關注、交流,禁止第三方擅自轉載。

相關文章