書接上回,Appium高級操作--其他操作-CSDN博客文章瀏覽閱讀182次,點贊6次,收藏7次。書接上回Appium高級操作--從源碼角度解析--模擬復雜手勢操作-CSDN博客。https://blog.csdn.net/fantasy_4/article/details/146162851主要講解了Appium的一些高級操作,比如基于ActionChain類的,操作系統API的方法等,便于解決比較復雜的場景下的手勢模擬。不過在自動化的過程中,經常會出現尋找查找元素時間過長,等待時間設置不合理導致腳本執行時間過程,最終的結果就是自動化運行的速度不如'手工'操作。為了解決上述問題,本篇文章主要講解一下Appium的等待機制
1.影響頁面加載時長的因素
做UI自動化的同學可能多數都會遇到這樣的問題,執行跳轉頁面后再去定位,經常會提示找不到元素,造成找不到元素的原因除了有元素定位本身的問題之外,還可能的原因是頁面本身的加載時長過長,那影響頁面加載時長的因素有哪些呢?
-
移動端性能
市面上出現生產使用的移動端各不相同,不同的配制,不同的機型,不同的系統,甚至不同的操作系統版本,因此在這些設備上安裝同一款軟件就會產生不同的加載時長。如果調試腳本時使用的是一款配置較好的手機,而實際運行腳本的是另外的配置較差的手機,就會出現明明調試的時候沒問題,正式運行就會出錯。
-
服務端性能
如果執行自動化的服務端還部署著其他服務,比如缺陷管理工具、代碼管理工具等,這樣服務端存在大量并發用戶請求,就會造成自動化執行時會花費更多的時間才能相應。
-
網絡因素
如果被測試APP中的Web頁面包含大量圖片,或者請求中存在低劣無效的代碼就會產生大量的數據請求,這是網絡的穩定至關重要。
2.強制等待
所謂強制等待,就是在執行自動化的過程中加上一個強制的等待時間,等待時間結束后期望要跳轉的頁面能夠加載完畢,如果沒有那可能還是要調整時間。
通常使用Python time
模塊的time.sleep(s)
來實現
優點:調試代碼時,能夠便于我們觀察腳本的執行情況
缺點:強制等待會導致執行腳本的時間不一致,設置的等待時間參差不齊,尤其是腳本很多時,就會造成腳本越執行越慢。
那么有沒有一種更智能的等待方式呢?能夠實現找到元素就立刻進行下一步操作,如果找不到元素就進行等待呢?
3.隱性等待
隱性等待就能夠完美解決上面提出的問題。
Appium的隱性等待繼承了Selenium的implicitly_wait()
方法。
driver.implicitly_wait(5)
# 隱性等待5s
優點:可以很智能判斷是否需要執行相應等待時長,一旦設置就會實例化整個會話的生命周期
缺點:會減緩測試速度(尤其是在需要查找某個元素不存在時的用例,會平白浪費時間等待查找該元素是不是存在),而且會干擾顯性等待,不建議使用隱形等待和顯性等待混用
4.顯性等待
顯性等待能夠更精細化的定制一些執行條件,等到條件滿足會后在進行下一步操作。
Appium并沒有引入Selenium的WebDriverWait類,因此要使用顯性等待,只能從Selenium中引入,主要由以下兩部分實現顯性等待
1)Selenium中的WebDriver類:定義超時時間、輪詢頻率等
2)Expected_conditions模塊:提供一些預期條件作為測試腳本進行后續操作的判斷依據
-
顯性等待整體語法結構
WebdriverWait(dirver, 超時時間,輪詢頻率, 忽略異常).until(可執行方法,超時后返回的信息)
上面代碼的含義:每隔一段時間,一定頻次,就會調用可執行方法,直到方法返回True,如果超時則返回超時后的信息。
注意until中的可執行方法必須是可以調用的,即這個對象一定有__call__()
方法
-
WebDriverWait類源碼分析
def __init__(self,driver: D,timeout: float,poll_frequency: float = POLL_FREQUENCY,ignored_exceptions: Optional[WaitExcTypes] = None, ):"""Constructor, takes a WebDriver instance and timeout in seconds. ?Attributes:----------driver- Instance of WebDriver (Ie, Firefox, Chrome or Remote) ora WebElement ?timeout- Number of seconds before timing out ?poll_frequency- Sleep interval between calls- By default, it is 0.5 second. ?ignored_exceptions- Iterable structure of exception classes ignored during calls.- By default, it contains NoSuchElementException only.
WebDriverWait類創建對象可以傳入4個參數:
-
driver:WebDriver實例化-必傳
-
timeout:超時時間-必傳
-
poll_frequency:輪詢頻率-非必傳
-
ignored_exceptions:可忽略異常-非必傳
WebDriverWait類提供兩個方法until()
和until_not()
,兩個方法傳參相同
-
method:可以調用方法-必傳,
-
str: 異常信息-非必傳
兩個方法的含義不同:
? | until | until_not |
---|---|---|
等待邏輯 | 等待條件變為 True | 等待條件變為 False 。 |
適用場景 | 等待元素出現或滿足特定條件。 | 等待元素消失或不滿足特定條件。 |
異常處理 | 如果條件未變為 True ,拋出 TimeoutException | 如果條件未變為 False ,拋出 TimeoutException |
返回值 | 返回滿足條件的對象(如元素) | 無返回值(僅等待條件變為 False ) |
(使用的Selenium是配套Appium-Python-Client一起下載的,版本是 4.29.0,其中until_not的方法注釋(定義部分)有錯誤)如下圖
-
Expected_conditions模塊
Expected_conditions模塊是selenium提供的各種預期條件,Expected_conditions有多種方法,黑體字相對比較常用
方法 | 描述 |
---|---|
title_is(title: str) | 判斷頁面的title和預期title是否一致,一直則返回True,否則返回False |
title_contains(title: str) | 判斷頁面的title是否包含預期title是否一致,大小寫敏感一直則返回True,否則返回False |
presence_of_element_located(locator: Tuple[str, str]) | 用于檢查某個元素是否存在于DOM中,但不一定可見,一旦找到元素則返回WebElement |
presence_of_all_elements_located(locator: Tuple[str, str]) | 用于檢查所有元素是否存在,如果存在返回所有匹配的元素的列表,否則報錯 |
url_matches(pattern: str) | 檢查當前driver的url是否包含字符串,包含返回True,不包含返回False |
url_to_be(url: str) | 檢查當前driver的url與預期值是否完全匹配,匹配返回True,否則返回False |
url_changes(url: str) | 檢查當前url和預期值是否一致,不一致返回True,一直返回False |
url_contains(url: str) | 檢查當前driver的url是否包含字符串,包含返回True,不包含返回False |
visibility_of(element: WebElement) | 參數是WebElement,判斷元素是否在當前頁面的DOM中并可見,若是返回True,否則返回False |
visibility_of_element_located(locator: Tuple[str, str] ) | 參數是locator,判斷元素是否在當前頁面的DOM中并可見,若是返回True,否則返回False |
visibility_of_any_elements_located(locator: Tuple[str, str]) | 至少能定位到一個可見元素,是返回列表 否則報錯 |
visibility_of_all_elements_located(locator: Tuple[str, str] ) | 找到所有符合條件的可見元素,是返回列表 否則報錯 |
invisibility_of_element_located(locator: Union[WebElement, Tuple[str, str]] ) | 判斷不可見元素是否存在 |
invisibility_of_element(element: Union[WebElement, Tuple[str, str]] ) | 判斷元素是都不可見 |
staleness_of(element: WebElement) | 判斷刷新后,元素是否仍然在DOM中,如果在返回False,否則返回True |
frame_to_be_available_and_switch_to_it(locator: Union[Tuple[str, str], str]) | 判斷frame_locator是否存在,存在則跳轉到對應frame并返回True,否則返回False |
text_to_be_present_in_element(locator: Tuple[str, str], text_: str) | 判斷text是否出現在元素中 |
text_to_be_present_in_element_value(locator: Tuple[str, str], text_: str ) | 判斷text是否出現在元素的value屬性中 |
text_to_be_present_in_element_attribute(locator: Tuple[str, str], attribute: str, text: str ) | 判斷text是否出現在元素的屬性中 |
element_to_be_clickable(mark: Union[WebElement, Tuple[str, str]] ) | 檢查元素是否可見并可以被單擊并且單擊 |
element_to_be_selected(element: WebElement) | 入參為WebElement,被定位的元素是否是被選中的,返回布爾值 |
element_located_to_be_selected(locator: Tuple[str, str]) | 入參為locator,被定位的元素是否是被選中的,返回布爾值 |
element_selection_state_to_be(element: WebElement, is_selected: bool) | 檢查給定元素是否是被選中的 |
element_located_selection_state_to_be(locator: Tuple[str, str], is_selected: bool ) | 查找元素并檢查指定的選擇狀態是否處于該狀態的期望,返回布爾值 |
new_window_is_opened(current_handles: List[str]) | 傳入當前窗口的句柄,判斷是否有新窗口打開,返回布爾值 |
number_of_windows_to_be(num_windows: int) | 判斷窗口數量是否符合預期 |
alert_is_present() | 判斷是否有alert,如果有,切換到alert,否則返回False |
-
代碼演示
from appium import webdriver from appium.options.android import UiAutomator2Options from appium.webdriver.common.appiumby import AppiumBy from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions ? desired_caps = {"platformName": "Android","deviceName": "XXXXXXXXXXXXX","appPackage": "com.sankuai.movie","appActivity": "com.sankuai.movie.MovieMainActivity","automationName": "UiAutomator2" } ? print("Desired Capabilities: ", desired_caps) ? driver = webdriver.Remote("http://localhost:4723", options=UiAutomator2Options().load_capabilities(desired_caps)) ? ? try:agree_id = "com.sankuai.movie:id/cyf"ele1 = WebDriverWait(driver, 10).until(expected_conditions.presence_of_element_located((AppiumBy.ID, agree_id)))ele1.click()my_id = 'com.sankuai.movie:id/b50'ele2 = WebDriverWait(driver, 10).until(expected_conditions.invisibility_of_element_located((AppiumBy.ID, my_id)))# WebDriverWait(driver, 10).until_not()ele2.click()ele4 = WebDriverWait(driver,10).until(expected_conditions.element_to_be_clickable((AppiumBy.ID, 'com.sankuai.movie:id/b50')))ele4.click() ? except Exception as e:raise e ? finally:# 關閉 Appium 會話driver.quit() ?
-
自定義等待條件
就是在expected_conditions模塊不滿足個需求的情況下,可以使用lambda表達式來自定義等待條件。
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
?
ele3 = WebDriverWait(driver, 10).until(lambda driver: driver.find_elements(AppiumBy.ID, 'com.sankuai.movie:id/b50'))
ele3.click()
5.總結三種等待區別
? | 強制等待 | 隱性等待 | 顯性等待 |
---|---|---|---|
實現方式 | time.sleep(3) | driver.implicitly_wait(5) | Webdriver類+expected_conditions模塊 |
靈活程度 | 不管是否找元素,必須等待響應時間 | 找到元素則不等待,否則等待 | 在固定之間 |
生命周期 | 當前行 | 整個會話 | 每個條件需要單獨設置 |
使用場景 | 腳本調試 | 頁面加載時間相對固定的全局等待場景,不建議跟顯性等待混用 | 動態頁面或需要等待特定條件的復雜場景,建議多使用 |
下一章介可能會講解一些關于自動化框架搭建相關內容,可以期待一下哦~
?