Selenium WebDriver:流畅的等待按预期工作,但隐含的等待不会

我是Selenium WebDriver的新手,并试图理解“等待”元素出现的正确方法。

我正在测试一个包含单选按钮答案的问题。 当您选择答案时,Javascript可以启用/禁用页面上的一些问题。

问题似乎是Selenium“点击得太快”而不是等待Javascript完成。 我试图用两种方法来解决这个问题 – 明确的等待解决了这个问题。 具体来说,这工作,并解决了我的问题:

private static WebElement findElement(final WebDriver driver, final By locator, final int timeoutSeconds) { FluentWait<WebDriver> wait = new FluentWait<WebDriver>(driver) .withTimeout(timeoutSeconds, TimeUnit.SECONDS) .pollingEvery(500, TimeUnit.MILLISECONDS) .ignoring(NoSuchElementException.class); return wait.until(new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver webDriver) { return driver.findElement(locator); } }); } 

但是,我宁愿使用隐式等待而不是这个。 我有我的网络驱动程序配置像这样:

 driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); 

这不能解决问题,我得到一个NoSuchElementException。 另外,我没有注意到10秒的停顿 – 它只是立即出错。 我已经验证了代码中的这一行正在用调试器命中。 我究竟做错了什么? 为什么不能等待元素出现,而FluentWait呢?

注意:正如我所提到的,我已经有了一个解决方法,我真的只是想知道为什么隐式等待不能解决我的问题。 谢谢。

请记住,几种情况之间是有区别的:

  • DOM中完全不存在的元素。
  • 存在于DOM中但不可见的元素。
  • 存在于DOM中但未启用的元素。 (即可点击)

我的猜测是,如果一些页面正在与JavaScript显示,元素已经存在于浏览器的DOM,但不可见。 隐含的等待只等待一个元素出现在DOM中,所以它立即返回,但是当你试图与元素交互时,你会得到一个NoSuchElementException异常。 你可以通过编写一个辅助方法来测试这个假设,这个辅助方法将等待一个元素的可见或可点击。

一些例子(在Java中):

 public WebElement getWhenVisible(By locator, int timeout) { WebElement element = null; WebDriverWait wait = new WebDriverWait(driver, timeout); element = wait.until(ExpectedConditions.visibilityOfElementLocated(locator)); return element; } public void clickWhenReady(By locator, int timeout) { WebDriverWait wait = new WebDriverWait(driver, timeout); WebElement element = wait.until(ExpectedConditions.elementToBeClickable(locator)); element.click(); } 

基本思路是:

明确的等待

 WebDriverWait.until(condition-that-finds-the-element); 

隐含的等待

 driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); 

换句话说,明确的是与某些条件相关联,而隐含着一些等待的东西。 看到这个链接

为了使工作流畅地等待尝试:

 public WebElement fluentWait(final By locator){ Wait<WebDriver> wait = new FluentWait<WebDriver>(driver) .withTimeout(30, TimeUnit.SECONDS) .pollingEvery(5, TimeUnit.SECONDS) .ignoring(NoSuchElementException.class); WebElement foo = wait.until( new Function<WebDriver, WebElement>() { public WebElement apply(WebDriver driver) { return driver.findElement(locator); } } ); return foo; }; 

希望这可以帮助)

警告一个常见的错误:

一旦你设置了隐式等待,你不能使用显式或流畅的等待,直到你重新设置隐式等待。 这意味着包含driver.findElement调用的ExpectedConditions将不会像预期的那样使用隐式等待! 你会经常遇到你想要检查一个元素或它的不存在的情况,但是你也不能这样做。

经过2年的经验和问题,我强烈建议不要使用隐含的等待。

我使用WebDriverWait类在C#中编写了一个小方法。 对我很好。

 public static void WaitForAjaxElement(IWebDriver driver, By byElement, double timeoutSeconds) { WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds)); wait.Until(x => x.FindElement(byElement)); } 

使用:

 WaitForAjaxElement(driver, By.ClassName("ui-menu-item"), 10); 

希望它有帮助。

来自Seleniumhq.com:

一个隐含的等待就是告诉WebDriver在查找一个或多个元素(如果不是立即可用的)时轮询DOM一段时间。 默认设置为0.一旦设置,隐式等待就设置为WebDriver对象实例的生命周期。

如果你发布你的测试代码,你真的想要做什么,我可以提供更多的信息。

https://stackoverflow.com/users/503060/hedley&#x7684; kotlin版本的答案:

 clickWhenReady("#suggest",10,driver) 

通过

 fun clickWhenReady(selector: String,timeout: Long, webdriver: WebDriver?) { val wait = WebDriverWait(webdriver, timeout); val element = wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector(selector))); element.click(); } 

我有另一个解决方案来解决这个问题(只适用于IE浏览器,我从来没有尝试其他浏览器):

1)创建Selenium驱动实例后,你可以得到它的COM实例

 Add-Type -Path .\SePSX.NET35\WebDriver.dll $ieDriver = New-Object "OpenQA.Selenium.IE.InternetExplorerDriver" $ieShell = $null $shell_apps = (New-Object -ComObject Shell.Application).Windows() foreach($app in $shell_apps) { if ($app.LocationURL -eq $ieDriver.URL) { $ieShell = $app break } } if ($ieShell -eq $null) { throw "Can't get WebDriver IE Instance" } 

2)每次调用GotoURL或点击动作后,检查$ ieShell.Busy状态,等待页面加载。

 $ieDriver.Navigate().GotoUrl("www.google.com") while ($ieShell.Busy -eq $true) {sleep 1} then call Selenium driver to get element id and do the further action $ieDriver.FindElementById ... 

使用这种方式,您不需要为Selenium设置页面加载和查找元素超时

 import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.FluentWait; import org.openqa.selenium.support.ui.Wait; FluentWait<WebDriver> wait = new FluentWait<WebDriver>(driver); wait.pollingEvery(250, TimeUnit.MILLISECONDS); wait.withTimeout(20, TimeUnit.SECONDS); wait.ignoring(NoSuchElementException.class); Predicate<WebDriver> predicate = new Predicate <WebDriver>() { public boolean apply(WebDriver arg0) { WebElement element = arg0.findElement(By.id("colorVar")); String color = element.getAttribute("color"); System.out.println("The color if the button is " + color); if(color.equals("blue")) { return true; } return false; } }; wait.until(predicate); 

下面是在c#.Net中使用DefaultWait的fluient等待代码。

  IWait<IWebDriver> wait = new DefaultWait<IWebDriver>(driver); wait.Timeout = TimeSpan.FromSeconds(10); wait.PollingInterval = TimeSpan.FromMilliseconds(100); IWebElement elementt = wait.Until<IWebElement>(ExpectedConditions.ElementIsVisible(By.Id("selectedfirstlast1"))); SelectElement se = new SelectElement(driver.FindElement(By.Id("selectedfirstlast1"))); element = se.SelectedOption; if (element.Text.Contains("Mumbai") && element.Selected) driver.FindElement(By.XPath("//table/tbody/tr[2]/td[7]/a")).Click();