【问题标题】:Selenium WebDriver: Fluent wait works as expected, but implicit wait does notSelenium WebDriver:流畅的等待按预期工作,但隐式等待没有
【发布时间】:2012-08-15 23:32:54
【问题描述】:

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

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

【问题讨论】:

标签: selenium webdriver


【解决方案1】:

我使用 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);

希望对你有帮助。

【讨论】:

    【解决方案2】:

    来自 Seleniumhq.com:

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

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

    【讨论】:

      【解决方案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;
      };
      

      希望这会有所帮助)

      【讨论】:

      • 如果 .withTimeout(30, TimeUnit.SECONDS) 已被弃用,请使用 .withTimeout(Duration.ofSeconds(20)
      • @paul,调整了答案。请仔细检查
      • 任何人都可以用 C# 编写以上内容吗?谢谢
      【解决方案4】:

      记住几个场景之间是有区别的:

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

      【讨论】:

      • 惊人的答案。 helpers 类总是可以节省时间。
      【解决方案5】:

      以下是在 c#.Net 中使用 DefaultWait 实现流畅等待的代码等价代码。

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

      【讨论】:

        【解决方案6】:

        常见错误的警告词:

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

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            【解决方案8】:

            我有另一个解决方案来解决这个问题(仅适用于 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 超时

            【讨论】:

              【解决方案9】:
              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-04-19
                • 2013-12-10
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-01-24
                • 2013-12-03
                相关资源
                最近更新 更多