WebDriver元素等待機制
能否構建健壯和可靠的測試是UI自動化測試能否成功的關鍵因素之一。但實際情況是當一個測試接著一個測試執行的時候,常會遇到各種不同的狀況。比如指令碼去定位元素或去驗證程式的執行狀態時,有時會發現找不到元素,這可能是由於突然的資源受限或網路延遲等引起響應速度太慢所導致,這時會返回測試失敗的結果。so我們需要在測試指令碼中引入延時機制,來使指令碼的執行速度與程式的響應速度相匹配。即使指令碼和程式的響應能夠同步。WebDriver為我們提供了隱式等待和顯式等待兩種機制。下面一一說明下:
隱式等待
隱式等待為WebDriver中的完整一個測試用例或者一組測試的同步,提供了通用的方法。對於解決由於網路延遲或利用Ajax動態載入所導致的程式響應時間不一致是非常有效的。
當設定了隱式等待時間後,WebDriver會在一定時間內持續檢測和搜尋DOM,以便於查詢一個或多個不立即載入成功可用的元素。一般情況下,隱式等待的預設超時間設定為0。但一旦設定會作用於這個WebDriver例項的整個生命週期或者說一次完整測試的執行期間,並且WebDriver會使其對所有測試步驟中包含的整個頁面的元素查詢時都有效。
WebDriver提供了implicity_wait()方法來配置超時時間。基於unittest寫的測試指令碼,常在setUp()方法中加入隱式等待時間並設定為30秒。當測試執行時,WebDriver在找不到一個元素時,將會等待30秒。當達到30秒超時時間後,將丟擲一個NoSuchElementException的異常。下面是一個用到隱式等待機制的簡單百度搜索測試指令碼,程式碼如下:
import unittest from selenium import webdriver class BaiduSearchTest(unittest.TestCase): def setUp(self): self.driver = webdriver.Chrome() self.driver.implicitly_wait(30) #set implicit wait time 30s self.driver.maximize_window() #open the baidu page self.driver.get('https://www.baidu.com') def test_search_python(self): #get the search textbox search_textbox = self.driver.find_element_by_id('kw') search_textbox.clear() #enter search keyword search_textbox.send_keys("python") #get the and seacrh button and click search_button = self.driver.find_element_by_id('su') search_button.click() #add assert tag = self.driver.find_element_by_link_text("PyPI").text self.assertEqual('PyPI',tag) def tearDown(self): #close the browser window self.driver.quit() if __name__ == '__main__': unittest.main(verbosity=2)
顯式等待
顯式等待是WebDriver中用於同步測試的另外一種等待機制。顯式等待比隱式等待具備更好的操控性。與隱式等待不同,顯示等待需要為指令碼設定一些預置或者定製化的條件,等待條件滿足後再進行下一步測試。
顯式等待可以只作用於僅有同步需要的測試用例。WebDriver提供了WebDriverWait類和expected_conditions類來實現顯式等待。expected_condition類提供了一些預置條件來作為測試指令碼進行下一步測試的判斷依據。下面是一個包含顯式等待的簡單測試指令碼,程式碼如下:
import unittest from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions class BaitduExplicitWaitTest(unittest.TestCase): def setUp(self): self.driver = webdriver.Chrome() self.driver.get('https://www.baidu.com') def test_login_link(self): WebDriverWait(self.driver, 10).until(lambda s: s.find_element_by_name("tj_login").get_attribute("class") == "lb") login_link = WebDriverWait(self.driver, 10).until(expected_conditions.element_to_be_clickable((By.LINK_TEXT, "登入"))) login_link.click() def tearDown(self): self.driver.quit() if __name__ == '__main__': unittest.main(verbosity=2)
上面指令碼先使用python的lambda表示式,並且基於WebDriverWait來實現自定義的預期條件判斷。設定顯式等待超時時間為10s,直到獲取到登入元素並判定其class屬性為1b。同時使用element_to_be_clickable方法來判斷預期條件是否滿足。該條件為等待通過定位器查詢的元素可見並可用,這裡為登入選項可以點選,直到最大等待時間10s。一旦根據指定的定位器找到了元素,預期條件判定方法會把元素返回給測試指令碼以提供給下一步的單擊操作。如果在設定的超時時間內,沒有通過定位器找到可見可點選的元素,將出丟擲TimeoutExpection異常。
下表是expected_conditions類支援的網頁瀏覽器自動化操作時常用到的一些通用等待條件。
expected_conditions類已經提供了多種內建的預期等待判定條件,我們在實際工作中的可以直接呼叫。但如果超出了expected_conditions的範圍,不用怕,WebDriverWait類也提供了強大的自定義預期等待判定功能。
注意:應儘量避免在測試中隱式等待與顯式等待混合使用來處理同步問題。