Python + Selenium(二十)等待

爲什麼需要等待?

自動化測試腳本在運行時,由於網絡原因、機器卡頓、頁面元素呈現等原因,導致定位失敗。定位失敗導致元素無法操作,獲取不到用於斷言的內容。

最終在檢查測試結果時就會出現很多因爲這些原因而導致的測試失敗,需要花大量精力來排查才能找到真正意義上的問題。

所以必須要使用等待。其實 Selenium 是有默認等待的,當你打開頁面時默認會等待頁面元素加載完畢才進行元素定位。但是頁面加載完畢後產生變化的元素則無法產生等待。

導致頁面產生變化的原因:

  • ajax 動態加載內容
  • JavaScript 某些動作改變 HTML 頁面元素:比如增加、刪除元素,隱藏與可見元素等

通常來說,我們經常會使用三種等待方式:

  • 強制等待
  • 隱式等待
  • 顯式等待(智能等待)

強制等待

這不是 Selenium 中提供的等待,而是使用 Python 標準庫 time 中的 sleep() 函數。

sleep(second) 函數,會將線程掛起。參數爲掛起的秒數。

程序運行時遇到 sleep() 函數就會停止設置的秒數,然後再運行下一句代碼。

相當於給後面的元素定位提供一定的緩衝時間。

import time

time.sleep(0.5)  # => 等待0.5秒

driver.find_element_by_id('test').click()

這種方式不適合大面積使用,這屬於硬性等待,不管元素是否能夠定位到, 都需要等待固定的時間。如果加得太多,會造成代碼中掛起時間過長,腳本運行效率降低。

只有當隱式等待和顯式等待同時失效時才能使用此方式。

隱式等待

隱式等待,之所以這樣叫,是因爲不像 sleep() 函數,要等哪裏就要在哪裏寫上一句;它只需要在初始生成瀏覽器驅動的時候加上就行,後續每個 find_element 類型的語句都會等待。

Sets a sticky timeout to implicitly wait for an element to be found,or a command to complete. This method only needs to be called one time per session.
設置隱含的時間用於等待元素查找和命令執行。每個 session 只需要調用此方法一次。
– Selenium 源碼對該方法的註釋

implicitly_wait(second) 方法用於實現這種隱式等待,參數表示等待超時的秒數。比如設置爲 30,表示每個 find_element 語句都會等待,最長等待 30s。30s 後如果 find_element 語句都沒有返回找到的元素則會拋出超時異常。

driver = webdriver.Chrome()
driver.implicitly_wait(30)  # => 超時30s

顯式等待

其實隱式等待已經作用於所有的 find_element 語句,爲何還需要顯式等待呢?

常見原因是某些情況下元素處於動態變化的過程,會導致查找失敗。

比如點擊菜單後菜單展開,鼠標懸停某元素後元素呈現:

顯式等待的用法如下:

from selenium.webdriver.common.by import By
#引入WebDriverWait
from selenium.webdriver.support.ui import WebDriverWait
#引入expected_conditions類,並重命名爲EC
from selenium.webdriver.support import expected_conditions as EC

#設置等待
wait = WebDriverWait(driver, 10, 0.5)
wait.until(EC.presence_of_element_located((By.ID, "kw")))  # => 這裏的(By.ID, "kw")組成一個元組

定位器 locator,在 Selenium 中把定位方式(如 By.ID, By.NAME 等)和 定位語句構成的元組稱爲定位器。

上面的例子的含義爲:根據設定的時間間隔檢查當前頁面 id 爲 “kw” 的元素是否存在,元素存在才進行下一步,如果超過設置時間檢測不到則拋異常。

說明:
顯式等待需要用到兩個類:
WebDriverWait 和 expected_conditions 中的條件類。WebDriverWait 用來執行等待過程,expected_conditions 中的類用來指定結束等待的條件。

先說 WebDriverWait:

WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)

參數說明:

  • dirver:瀏覽器驅動
  • Timeout:最長超時時間,默認以秒爲單位
  • poll_frequency:檢測的間隔步長,默認爲0.5s
  • ignored_exceptions:超時後的拋出的異常類型,默認拋出 NoSuchElementException 異常。

對 WebDriverWait 進行實例化後,需要調用 until 方法進行實際的等待:

until(method, message='')

參數說明:

  • method:接收一個函數或方法作爲參數,用來作爲等待的條件
  • message:等待失敗後在拋出的異常中顯示的異常信息

作爲條件的 expected_conditions 類:

用於設置一些預期條件,比如元素是否加載出來、元素是否可見等等。

expected_conditions 中提供的各種條件:

條件 說明
title_is 判斷當前頁面的title是否精確等於預期
title_contains 判斷當前頁面的title是否包含預期字符串
presence_of_element_located 判斷某個元素是否被加到了 DOM 樹裏,並不代表該元素一定可見;
常用的條件,用於判斷一個元素是否能被 find_element 找到
visibility_of_element_located 判斷某個元素是否可見;
可見代表元素非隱藏,並且元素的寬和高都不等於0;
常用的條件,用於判斷元素是否可見
visibility_of 與 visibility_of_element_located 作用一致;
接收的參數是元素對象而非定位器
presence_of_all_elements_located 判斷是否至少有1個元素存在於DOM樹中;
舉個例子,如果頁面上有n個元素的class都是’column-md-3’,那麼只要有1個元素存在,這個方法就返回True
text_to_be_present_in_element 判斷某個元素中的text是否包含了預期的字符串
text_to_be_present_in_element_value 判斷某個元素中的value屬性是否包含了預期的字符串
frame_to_be_available_and_switch_to_it 判斷該frame是否可以switch進去,如果可以的話,返回True並且switch進去,否則返回False
invisibility_of_element_located 判斷某個元素中是否不存在於DOM樹或不可見
element_to_be_clickable 判斷某個元素中是否可見並且是enable的,這樣的話才叫clickable
staleness_of 等某個元素從DOM樹中移除,注意,這個方法也是返回True或False
element_to_be_selected 判斷某個元素是否被選中了,一般用在下拉列表
element_selection_state_to_be 判斷某個元素的選中狀態是否符合預期
element_located_selection_state_to_be 跟上面的方法作用一樣,只是上面的方法傳入定位到的element,而這個方法傳入定位器
alert_is_present 判斷頁面上是否存在alert

顯式等待是一種智能程度較高的等待方式,可以有效的增強腳本的健壯性。可以按自己的需要稍微封裝一下,避免在用的時候引入很多內容。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章