问题描述
当我们加载页面后,可能需要等待页面渲染,等待某个 HTML 元素加载完成。我们经常使用 Thread.sleep() 进行等待,但是具有以下缺点:
1)等待时间过长,而页面已经加载完成;等待时间过短,而页面还未加载完成;
2)我们无法确定要等待的具体时间。如果使用 while 循环检查,程序会显得“不整洁”;
3)每个查找元素的地方都需要等待;
4)必须等待特定时间后,即 Thread.sleep() 设置的时间,才能继续执行后续程序;
解决方案
我们可以使用 Selenium 提供的等待方法:
1)Implicit wait – Provided by Selenium WebDriver
2)Explicit wait (WebDriverWait & FluentWait) Provided by Selenium WebDriver
3)Fluent Wait
Implicit Wait
如下是演示代码(只包含关键部分),我们通过演示代码进行讲解:
WebDriver driver=new ChromeDriver(); driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); driver.get("https://www.easemytrip.com/"); driver.findElement(By.id("FromSector_show")).sendKeys("Delhi", Keys.ENTER); driver.findElement(By.id("Editbox13_show")).sendKeys("Mumbai", Keys.ENTER);
如上示例,使用 implicitlyWait 最多 30s 等待,具有以下优势:
1)在 findElement 时,最多 30s 等待,只要找元素就立即向下执行;
2)如果在 30s 内没有找到,则返回 ElementNotVisibleException 异常;
3)全局设置(只需要设置一次,无需在每次查找元素时进行设置);
但是我们会遇到另外场景,比如:虽然 HTML 元素已经找到,但是在页面元素是否可见、是否可以点击,这些会影响自动化测试的进行。针对这个问题,我们可以使用 Explicit wait 等待。
Explicit wait
如下是演示代码(只包含关键部分),我们通过演示代码进行讲解:
WebDriver driver = new ChromeDriver(); driver.get("https://www.rentomojo.com/"); // 等待页面元素可见 WebDriverWait wait = new WebDriverWait(driver, 120); wait.until(ExpectedConditions.visibilityOf(driver.findElement(By.xpath("//div[@class='Campaign__innerWrapper']/button")))); driver.findElement(By.xpath("//div[@class='Campaign__innerWrapper']/button")).click(); // 等待 body 中出现内容 // https://stackoverflow.com/questions/15656252/wait-till-text-present-in-text-field/15657053 new WebDriverWait(driver, 120).until(new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver input) { WebElement bodyElement = input.findElement(By.xpath("html/body")); return !"".equals(bodyElement.getAttribute("innerHTML").trim()); } });
如上程序,通过 visibilityOf 方法等待,直到特定元素可见。通过该方法可以判断某些 HTML 元素是否已经处于特定状态。还有很多其他状态,参考 ExpectedConditions 文档。
Fluent Wait
类似与 Explicit wait 等待,但是更加灵活,可以自定义等待时间粒度、忽略异常等等:
Wait<WebDriver> fluentWait = new FluentWait<WebDriver>(driver) .withTimeout(60, TimeUnit.SECONDS) // // this defines the polling frequency .pollingEvery(2, TimeUnit.SECONDS) .ignoring(NoSuchElementException.class); // this defines the exception to ignore WebElement foo = fluentWait.until(new Function<WebDriver, WebElement>() { // in this method defined your own subjected conditions for which // we need to wait for public WebElement apply(WebDriver driver) { return driver.findElement(By.id("foo")); } });
注意事项,我们没有使用过 Fluent Wait 等待,这里只是简单整理,详细方法需要参考官方文档。
参考文献
Using Thread.sleep() in Selenium WebDriver – Make Selenium Easy