[跟著官方文檔學Selenium][學習筆記][七][WebDriver的等待]
WebDriver通常可以說有一個阻塞API。因為它是一個指示瀏覽器做什么的進程外庫,而且web平臺本質上是異步的,所以WebDriver不跟蹤DOM的實時活動狀態。這伴隨著一些我們將在這里討論的挑戰。
根據經驗,大多數由于使用Selenium和WebDriver而產生的間歇性問題都與瀏覽器和用戶指令之間的競爭條件有關。例如,用戶指示瀏覽器導航到一個頁面,然后再試圖查找元素時得到一個no such element的錯誤。
這個WebDriver的說明可能看起來很簡單:
import org.junit.Assert; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.edge.EdgeDriver; public class demo1 { public static void main(String[] args) { WebDriver webDriver = new EdgeDriver(); webDriver.get("XXX\race_condition.html"); WebElement element = webDriver.findElement(By.tagName("p")); Assert.assertEquals(element.getText(),"Hello from JavaScript"); /* * Exception in thread "main" org.junit.ComparisonFailure: * expected:<...ello from JavaScript[!]> but was:<...ello from JavaScript[]> * */ } }
這里的問題是WebDriver中使用的默認頁面加載策略聽從document.readyState在返回調用navigate之前將狀態改為complete。因為p元素是在文檔完成加載之后添加的,所以這個WebDriver腳本可能是間歇性的。它可能間歇性是因為無法做出保證說異步觸發這些元素或事件不需要顯式等待或阻塞這些事件。
幸運的是,WebElement接口上可用的正常指令集-例如WebElement.click和WebElement.sendKeys是保證同步的,因為直到命令在瀏覽器中被完成之前函數調用是不會返回的(或者回調是不會在回調形式的語言中觸發的)。高級用戶交互APIs,鍵盤和鼠標例外,因為它們被明確地設計為"按我說的做"的異步命令。
等待是在繼續下一步之前會執行一個自動化任務來消耗一定的時間。
為了克服瀏覽器和WebDriver腳本之間的競爭問題,大多數Selenium客戶都附帶了一個wait包。在使用等待時,使用的是通常所說的顯式等待。
顯式等待
顯式等待是Selenium客戶可以使用的命令式過程語言。它們允許你的代碼暫停程序執行,或凍結線程,直到滿足通過的條件。這個條件會以一定頻率一直被調用,直到等待超時。這意味著只要條件返回一個假值,它就會一直嘗試和等待。
由于顯式等待允許你等待條件的發生,所以它們非常適合在瀏覽器及其DOM和WebDriver腳本之間同步狀態。
為了彌補我們之前的錯誤指令集,可以使用等待來讓findElement調用等待直到腳本中動態添加的元素被添加到DOM中。
import org.openqa.selenium.By; import org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.edge.EdgeDriver; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import java.time.Duration; public class demo2 { public static void main(String[] args) { WebDriver webDriver = new EdgeDriver(); webDriver.get("https://www.baidu.com"); webDriver.findElement(By.id("kw")).sendKeys("Selenium"+ Keys.ENTER); //初始化等待時間為10秒,直到按鍵可以點擊 WebElement firstResult = new WebDriverWait(webDriver, Duration.ofSeconds(5)) .until(ExpectedConditions.elementToBeClickable(By.id("su"))); //打印結果 System.out.println(firstResult.getAttribute("value"));//百度一下 } }
我們將條件作為函數引用傳遞,等待將會重復運行直到其返回值為true。"truthful"返回值是在當前語言中計算為boolean true的任何值,例如字符串、數字、boolean、對象(包括WebElement)或填充(非空)的序列或列表。這意味著空列表的計算結果為false。當條件為true且阻塞等待終止時,條件的返回值將成為等待的返回值。
有了這些知識,并且因為等待實用程序默認情況下會忽略no such element的錯誤,所以我們可以重構我們的指令使其更簡潔。
import org.junit.Assert; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.edge.EdgeDriver; import org.openqa.selenium.support.ui.WebDriverWait; import java.time.Duration; public class demo3 { public static void main(String[] args) { WebDriver webDriver = new EdgeDriver(); webDriver.get("XXX\\race_condition.html"); WebElement foo = new WebDriverWait(webDriver, Duration.ofSeconds(6)) .until(webDriver1 -> webDriver1.findElement(By.tagName("p"))); //Variable 'webDriver' is already defined in the scope Assert.assertEquals(foo.getText(),"Hello from JavaScript!"); } }
在這個示例中,我們傳遞一個匿名函數(但是我們也可以像前面那樣顯式地定義它,以便重用它)。傳遞給我們條件的第一個,也是唯一的一個參數始終是對驅動程序對象WebDriver的引用。在多線程環境中,應該小心操作傳入條件的驅動程序引用,而不是外部范圍中對驅動程序的引用。
因為等待將會吞沒在沒有找到元素時引發的no such element的錯誤,這個條件會一直重試直到找到元素為止。然后它將獲取一個WebElement的返回值,并將其傳遞回我們的腳本。
如果條件失敗,例如從未得到條件為真實的返回值,等待將會拋出/引發一個叫timeout error的錯誤/異常。
選項
等待條件可以根據你的需要進行定制。有時候是沒有必要等待缺省超時的全部范圍,因為沒有達到成功條件的代價可能很高。
等待允許你傳入一個參數來覆蓋超時:
new WebDriverWait(driver, Duration.ofSeconds(3)).until(ExpectedConditions.elementToBeClickable(By.xpath("http://a/h3")));
預期的條件
由于必須同步DOM和指令是相當常見的情況,所以大多數客戶端還附帶一組預定義的預期條件。顧名思義,它們是為頻繁等待操作預定義的條件。
不同的語言綁定提供的條件各不相同,可以參開每個客戶端綁定的API文檔:
Java:https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html
Python:https://seleniumhq.github.io/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.expected_conditions.html?highlight=expected
隱式等待
還有第二種區別于顯示等待類型的隱式等待。通過隱式等待,WebDriver在試圖查找任何元素時在一定時間內輪詢DOM。當網頁上的某些元素不是立即可用并且需要一些時間來加載時是很有用的。
默認情況下隱式等待元素出現是禁用的,需要在單個會話的基礎上手動啟用。將顯示等待和隱式等待混合在一起會導致意想不到的結果,就是說即使元素可用或條件為真也要等待睡眠的最長時間。
警告:不要混合使用隱式和顯式等待。這樣做會導致不可預測的等待時間。例如:將隱式等待設置為10秒,將顯式等待設置為15秒,可能會導致在20秒后發生超時。
隱式等待是告訴WebDriver如果在查找一個或多個不是立即可用的元素時輪詢DOM一段時間。默認設置為0,表示禁用。一旦設置好,隱式等待就被設置為會話的生命周期。
import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.edge.EdgeDriver; import java.time.Duration; public class demo5 { public static void main(String[] args) { WebDriver driver = new EdgeDriver(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); driver.get("http://somedomain/url_that_delays_loading"); WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement")); } }
流暢等待
流暢等待實例定義了等待條件的最大時間量,以及檢查條件的頻率。
用戶可以配置等待來忽略等待時出現的特定類型的異常,例如在頁面上搜索元素時出現的NoSuchElementException。
import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.edge.EdgeDriver; import org.openqa.selenium.support.ui.FluentWait; import org.openqa.selenium.support.ui.Wait; import java.time.Duration; import java.util.function.Function; public class demo6 { public static void main(String[] args) { WebDriver webDriver = new EdgeDriver(); webDriver.get("https://www.example.com"); // Waiting 10 seconds for an element to be present on the page, checking // for its presence once every 2 seconds. Wait
Selenium
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。