27

我是 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 会?

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

4

9 回答 9

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();
}
于 2013-01-25T10:19:39.883 回答
3

基本思路如下:

显式等待

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

隐式等待

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

换句话说,显式与要保持的某些条件相关联,而隐式与等待某事的时间相关联。 看到这个链接

为了使工作 fluentWait 正确尝试这个:

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;
};

希望这可以帮助)

于 2012-09-05T15:16:19.440 回答
2

一个常见错误的警告词:

一旦你设置了隐式等待,你就不能使用显式等待或流式等待,直到你再次重置隐式等待。这意味着ExpectedConditions, 其中包含driver.findElement调用将无法按预期使用隐式等待!您经常会遇到想要立即检查某个元素或其不存在的情况——但您也不能这样做。

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

于 2015-11-11T09:43:15.480 回答
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();
}
于 2016-08-09T16:34:09.557 回答
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);

希望能帮助到你。

于 2012-08-22T13:17:59.830 回答
0

来自 Seleniumhq.com:

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

如果您发布您真正想做的测试代码,我可以提供更多信息。

于 2012-08-22T13:23:24.257 回答
0

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

1)创建Selenium驱动实例后,可以得到它的ie 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 超时

于 2017-02-07T09:17:46.083 回答
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);
于 2017-10-10T12:03:03.743 回答
-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();
于 2015-06-03T06:37:25.897 回答