我是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秒的暂停 - 它只是立即出错。我已经验证了代码中的这一行是用调试器命中的。我究竟做错了什么?为什么implicitlyWait不等待元素出现,但FluentWait呢?
注意:正如我所提到的,我已经有了解决方法,我真的只想知道为什么隐式等待不能解决我的问题。感谢。
答案 0 :(得分:27)
请记住,几种情况之间存在差异:
我的猜测是,如果某些页面是用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();
}
答案 1 :(得分:3)
基本理念如下:
明确等待
WebDriverWait.until(condition-that-finds-the-element);
隐含等待
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
换句话说,显式与某些条件相关联,而隐含一段时间等待某事。 see this link
要使工作流畅,请尝试以下方法:
public WebElement fluentWait(final By locator){
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(Duration.ofSeconds(30))
.pollingEvery(Duration.ofMillis(100))
.ignoring(NoSuchElementException.class);
WebElement foo = wait.until(
new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
return driver.findElement(locator);
}
}
);
return foo;
};
希望这有帮助)
答案 2 :(得分:2)
警告常见错误:
设置隐式等待后,您无法使用显式或流畅等待,直到再次重置隐式等待。这意味着包含ExpectedConditions
调用的driver.findElement
将无法按预期执行隐式等待!您经常会遇到想要立即检查元素或其不存在的情况 - 但您也不能这样做。
经过约2年的经验和问题,我强烈建议不要使用隐式等待。
答案 3 :(得分:1)
https://stackoverflow.com/users/503060/hedley答案的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();
}
答案 4 :(得分:0)
我使用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);
希望它有所帮助。
答案 5 :(得分:0)
来自Seleniumhq.com:
隐式等待是告诉WebDriver在尝试查找一个或多个元素(如果它们不是立即可用)时轮询DOM一段时间。默认设置为0.设置后,将为WebDriver对象实例的生命周期设置隐式等待。
如果您发布测试代码,您可以提供更多信息。
答案 6 :(得分:0)
我有另一个解决方案来解决这个问题(仅适用于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设置页面加载和findElement超时
答案 7 :(得分:0)
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);
答案 8 :(得分:-1)
以下是使用DefaultWait在c#.Net中进行流行等待的代码等效代码。
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();