# Waits 自動測試 ###### tags: `Shannon`,`Test` # 未知 - out-of-process library # 介紹 通常WebDriver會被說有一個會blocking的API,`waits`會指示browser要做甚麼,他是一個out-of-process的library,WebDriver不會去追蹤動作或是目前的DOM狀態 舉個例子 > document ```typescript <!doctype html> <meta charset=utf-8> <title>Race Condition Example</title> <script> var initialised = false; window.addEventListener("load", function() { var newElement = document.createElement("p"); newElement.textContent = "Hello from JavaScript!"; document.body.appendChild(newElement); initialised = true; }); </script> ``` > WebDriver ```typescript driver.get("file:///race_condition.html"); WebElement element = driver.findElement(By.tagName("p")); assertEquals(element.getText(), "Hello from JavaScript!"); ``` WebDriver是看document.readyState是否是complete,上面的例子`<p></p>`會因為document已經loading進去了(driver.get)所以來不及加進去,這時候webdriver會就混亂找不到p,因此我們應該要等待他載完,再來呼叫Driver去找p,有幾種方法。 ## Explicit wait 明確等待 你會設定條件,直到條件符合之前,它會讓你的程序暫停或定節。但如果一直沒有符合條件,直到超過的規定時間,他就會傳回錯誤。你可以等待條件發生後再執行,因此很適合用在DOM和WebDriver之間的同步 ```typescript WebDriver driver = new ChromeDriver(); driver.get("https://google.com/ncr"); driver.findElement(By.name("q")).sendKeys("cheese" + Keys.ENTER); // Initialize and wait till element(link) became clickable - timeout in 10 seconds WebElement firstResult = new WebDriverWait(driver, Duration.ofSeconds(10)).until(ExpectedConditions.elementToBeClickable(By.xpath("//a/h3"))); // Print the first result System.out.println(firstResult.getText()); ``` > `WebElement firstResult = new WebDriverWait(driver, Duration.ofSeconds(10)).until(ExpectedConditions.elementToBeClickable(By.xpath("//a/h3")))` * 將條件作為函數引用進去,這個wait會一直重複執行直到其返回值為true,但當條件為false或是超時,條件的返回值就會成為wait的返回值。 ## Implicit Wait 隱式等待 WebDriver再找任何element的時候都會詢問DOM,當網頁上某些元素無法立即使用並且需要一些時間加載時就很好用。 默認的狀態下,implicit wait是不能用的,必須透過session啟用,但千萬不要跟explicit混用,可能會導致無法預測的等待時間。inplicit wait告訴webDriver在找element的時候,去詢問DOM for a certain amount of time. ```typescript WebDriver driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); driver.get("http://somedomain/url_that_delays_loading"); WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement")); ``` ## FluentWait 流利等待 他定義了等待條件的最長時間,以及檢查條件的頻率。他可以設定說忽略某些exception像是`NoSuchElementException` when searching for an element on the page. ```typescript // Waiting 30 seconds for an element to be present on the page, checking // for its presence once every 5 seconds. Wait<WebDriver> wait = new FluentWait<WebDriver>(driver) .withTimeout(Duration.ofSeconds(30)) .pollingEvery(Duration.ofSeconds(5)) .ignoring(NoSuchElementException.class); WebElement foo = wait.until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { return driver.findElement(By.id("foo")); } }); ``` > 更簡潔 ```typescript WebElement foo = new WebDriverWait(driver, Duration.ofSeconds(3)) .until(driver -> driver.findElement(By.name("q"))); assertEquals(foo.getText(), "Hello from JavaScript!"); ``` ## 程式碼改編 不用使用Thread.sleep 1. 使用Implicit Wait` driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); ` ```typescript @BeforeAll public static void beforeAll(){ WebDriverManager.chromedriver().setup(); driver = new ChromeDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); } ``` 2. 使用Explicit Wait # 補充: Page Loading Strategy 當Selenium WebDriver載入一個要loading很久的畫面時,通常會建議不要下載額外的資源,像是images, css, js等等。 * `document.readyState`一個document描述的屬性,會顯示目前document loading的狀態。 * by default, webDriver在回應`driver.get()`or`driver.navigate().to()`的時候會延遲停止,直到document state is complete. * In SPA application(like Angular, react, Ember), 一但動態內容載入完畢(pageloadstrategy status is COMPLETE),就算click link or do sth action,網頁都不會寄request to server,因為內容是在客戶端加載的,沒有刷新頁面。SPA Application可以不透過任何server request去load很多動態的view,因此pageloadstrategy總是顯示`complete`status直到我們做一個新的driver.get()或是driver.navigate().to() ## normal * 它會讓Selenium WebDriver去等待,直到所有的頁面載入完畢!! * selenium會等待整個介面加載完成(html, 子資源的下載與解析ex. js, images) * When set to normal, selenium webdriver waits until the load event fire is returned. ```typescript import org.openqa.selenium.PageLoadStrategy; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.chrome.ChromeDriver; public class pageLoadStrategy { public static void main(String[] args) { ChromeOptions chromeOptions = new ChromeOptions(); chromeOptions.setPageLoadStrategy(PageLoadStrategy.NORMAL); WebDriver driver = new ChromeDriver(chromeOptions); try { // Navigate to Url driver.get("https://google.com"); } finally { driver.quit(); } } } ``` ## eager * 如果使用`PageLoadStrategy.EAGER`他就會讓selenium webdriver去等待直到原本的html document已經完全載入並解析,而且拋棄載入stylesheets, images, subframs * 簡單來說: 等待整個DOM tree加載完成, ```typescript import org.openqa.selenium.PageLoadStrategy; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.chrome.ChromeDriver; public class pageLoadStrategy { public static void main(String[] args) { ChromeOptions chromeOptions = new ChromeOptions(); chromeOptions.setPageLoadStrategy(PageLoadStrategy.EAGER); WebDriver driver = new ChromeDriver(chromeOptions); try { // Navigate to Url driver.get("https://google.com"); } finally { driver.quit(); } } } ``` ## none * `PageLoadStrategy.NONE`當設定成none後selenium webdriver只會等帶到page下載完畢 * 當html下載完成之後,不需等待解析完成selenim會直接返回 ```typescript import org.openqa.selenium.PageLoadStrategy; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.chrome.ChromeDriver; public class pageLoadStrategy { public static void main(String[] args) { ChromeOptions chromeOptions = new ChromeOptions(); chromeOptions.setPageLoadStrategy(PageLoadStrategy.NONE); WebDriver driver = new ChromeDriver(chromeOptions); try { // Navigate to Url driver.get("https://google.com"); } finally { driver.quit(); } } } ``` ## selenium的幾種等待方式 1. Thread.sleep(1000) 2. 使用WebDriver wait * 最多等待10秒,一旦等到XXX元素出现即退出等待执行后面操作,10秒后仍然没有出现XXX元素则抛出异常。 ```typescript WebDriverWait wait=new WebDriverWait(driver,10); wait.until(ExpectedConditions.presenceOfElementLocated(By.id("XXX"))); ``` 3. 所有的findElement方式都會隱式等待10s ```typescript driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS); ``` 網頁來源: * https://www.cnblogs.com/mabingxue/p/11057366.html * https://www.selenium.dev/documentation/en/webdriver/page_loading_strategy/ # 補充: DOM是甚麼? Document Object Model 簡單來說就是把HTML文件內的各個標籤,像是文字或是圖片等定義成物件,這些物件會形成樹狀結構(如下) ![](https://www.w3schools.com/js/pic_htmltree.gif) ## 為何需要DOM 瀏覽器就像一個編譯器,去負責編譯我們寫的網頁程式,通常會先訂好一個規則讓各個瀏覽器按照規則去編譯我們的程式,W3C就定義了很多網頁的規則讓大家可以依照規則去設計瀏覽器,其中DOM就是其中一個規則。 ## DOM 解析 DOM就是一個樹狀結構,最重要的一個重點就是node節點。在DOM裡面每個element, text都是一個節點 > 節點通常分為以下四點 * Document * HTML檔案的開端 所有的一切從document開始往下執行 * Element * 標籤 html tag * Text * 被標籤包起來的文字 * Attribute * 各個標籤的相關屬性, class, id 等等 > html ```typescript <html> <head> <title>example</title> </head> <body> <h1 class="txt">Hello World</h1> </body> </html> ``` > DOM Tree ![](https://i.imgur.com/quZtbSB.jpg) ## DOM API * 通常就是透過element的屬性去抓取元素 資源:https://ithelp.ithome.com.tw/articles/10202689