Selenium WebDriver:Fluent等待按预期工作,但隐式等待没有

时间:2012-08-20 15:53:53

标签: 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秒的暂停 - 它只是立即出错。我已经验证了代码中的这一行是用调试器命中的。我究竟做错了什么?为什么implicitlyWait不等待元素出现,但FluentWait呢?

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

9 个答案:

答案 0 :(得分:27)

请记住,几种情况之间存在差异:

  • 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();
}

答案 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();