【问题标题】:Selenium C# WebDriver: Wait until element is presentSelenium C# WebDriver:等到元素出现
【发布时间】:2011-10-23 00:11:12
【问题描述】:

我想确保在 webdriver 开始执行操作之前元素存在。

我正试图让这样的事情发挥作用:

WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, 5));
wait.Until(By.Id("login"));

我主要是在纠结如何设置匿名函数...

【问题讨论】:

  • 仅供参考 - 像 TimeSpan.FromSeconds(5) 这样构建你的时间跨度会更干净。它使 IMO 更加清晰

标签: c# selenium selenium-webdriver webdriver automated-tests


【解决方案1】:

使用 solution provided by Mike Kwan 可能会对整体测试性能产生影响,因为所有 FindElement 调用都将使用隐式等待。

很多时候,您会希望 FindElement 在元素不存在时立即失败(您正在测试格式错误的页面、缺少元素等)。通过隐式等待,这些操作将在抛出异常之前等待整个超时到期。默认的隐式等待设置为 0 秒。

我已经为 IWebDriver 编写了一个小扩展方法,它为FindElement() 方法添加了一个超时(以秒为单位)参数。这是不言自明的:

public static class WebDriverExtensions
{
    public static IWebElement FindElement(this IWebDriver driver, By by, int timeoutInSeconds)
    {
        if (timeoutInSeconds > 0)
        {
            var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
            return wait.Until(drv => drv.FindElement(by));
        }
        return driver.FindElement(by);
    }
}

我没有缓存 WebDriverWait 对象,因为它的创建非常便宜,这个扩展可以同时用于不同的 WebDriver 对象,我只在最终需要时进行优化。

用法很简单:

var driver = new FirefoxDriver();
driver.Navigate().GoToUrl("http://localhost/mypage");
var btn = driver.FindElement(By.CssSelector("#login_button"));
btn.Click();
var employeeLabel = driver.FindElement(By.CssSelector("#VCC_VSL"), 10);
Assert.AreEqual("Employee", employeeLabel.Text);
driver.Close();

【讨论】:

  • 如果有人想知道,WebDriverWait 来自 OpenQA.Selenium.Support.UI 命名空间,并位于 NuGet 上名为 Selenium WebDriver Support Classes 的单独包中
  • @Ved 我可以吻你
  • @Loudenvier 请将第一行加粗,以便更明显。特别是因为它不是公认的答案,尽管它是一种更好、更精确的方法。
  • Selenium WebDriver Support Classes 现在在 NuGet 上显示为 "Selenium.Support",当前版本为 3.4.0
  • 在我使用这条线 return wait.Until(ExpectedConditions.ElementToBeClickable(by)); 之前,我仍然有很多错误,现在它工作得很好。请注意以防其他人得到仍未找到的随机元素。
【解决方案2】:

您也可以使用隐式等待:

driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);

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

【讨论】:

  • 谢谢,新语法是:driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
  • @RedaBalkouch,迈克在他的回答中使用的语法是正确的。这是 C#
  • 如果您选择使用隐式等待,请注意不要使用显式等待。这可能会导致一些不可预测的行为,从而导致糟糕的测试结果。一般来说,我建议使用显式等待而不是隐式等待。
  • 此方法现已弃用,您应该改用属性 ImplicitWait : Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
  • 我使用了提供的方法,发现该方法已被 Samuel 指出已弃用。检查项目是否存在现在等待指定的时间。
【解决方案3】:

你也可以使用

ExpectedConditions.ElementExists

因此,您将搜索这样的元素可用性

new WebDriverWait(driver, TimeSpan.FromSeconds(timeOut)).Until(ExpectedConditions.ElementExists((By.Id(login))));

Source

【讨论】:

  • 同意,这比单纯的超时有用得多(在动态加载对象的情况下)。
  • 这行得通。它现在被标记为已弃用,因此应避免使用。
  • 这是新方法(未弃用):stackoverflow.com/a/49867605/331281
  • 请注意,此时,DotNetSeleniumExtras.WaitHelpers(上面由@Dejan 引用)“未维护,问题将不会修复,PR 将不会被接受”。 (来源:github.com/SeleniumHQ/selenium/issues/…)。它的出版商正在寻找维护者从他那里接管它。
  • 链接已损坏 (404)。
【解决方案4】:

这是Loudenvier's solution 的一个变体,它也适用于获取多个元素:

public static class WebDriverExtensions
{
    public static IWebElement FindElement(this IWebDriver driver, By by, int timeoutInSeconds)
    {
        if (timeoutInSeconds > 0)
        {
            var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
            return wait.Until(drv => drv.FindElement(by));
        }
        return driver.FindElement(by);
    }

    public static ReadOnlyCollection<IWebElement> FindElements(this IWebDriver driver, By by, int timeoutInSeconds)
    {
        if (timeoutInSeconds > 0)
        {
            var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
            return wait.Until(drv => (drv.FindElements(by).Count > 0) ? drv.FindElements(by) : null);
        }
        return driver.FindElements(by);
    }
}

【讨论】:

  • 不错!我刚刚将它添加到我自己的库中!这就是共享代码的美妙之处!!!
  • 我建议添加一个。您可以捕获 NoSuchElement 解决方案并在该实例中返回 null。然后你可以创建一个名为 .exists 的扩展方法,它返回 true,除非 IWebElement 为 null。
【解决方案5】:

Loudenvier's solution 的启发,这是一个适用于所有 ISearchContext 对象的扩展方法,而不仅仅是 IWebDriver,它是前者的特化。该方法还支持等到元素显示出来。

static class WebDriverExtensions
{
    /// <summary>
    /// Find an element, waiting until a timeout is reached if necessary.
    /// </summary>
    /// <param name="context">The search context.</param>
    /// <param name="by">Method to find elements.</param>
    /// <param name="timeout">How many seconds to wait.</param>
    /// <param name="displayed">Require the element to be displayed?</param>
    /// <returns>The found element.</returns>
    public static IWebElement FindElement(this ISearchContext context, By by, uint timeout, bool displayed=false)
    {
        var wait = new DefaultWait<ISearchContext>(context);
        wait.Timeout = TimeSpan.FromSeconds(timeout);
        wait.IgnoreExceptionTypes(typeof(NoSuchElementException));
        return wait.Until(ctx => {
            var elem = ctx.FindElement(by);
            if (displayed && !elem.Displayed)
                return null;

            return elem;
        });
    }
}

示例用法:

var driver = new FirefoxDriver();
driver.Navigate().GoToUrl("http://localhost");
var main = driver.FindElement(By.Id("main"));
var btn = main.FindElement(By.Id("button"));
btn.Click();
var dialog = main.FindElement(By.Id("dialog"), 5, displayed: true);
Assert.AreEqual("My Dialog", dialog.Text);
driver.Close();

【讨论】:

  • 如果您设置了隐式等待,例如 _webDriver.Manage().Timeouts().ImplicitlyWait(Timeout);,它仍然会超过您在此处设置的超时值。
  • 这似乎对我不起作用...?我在对扩展方法的调用周围添加了一个Stopwatch,并在发送到Until() 的lambda 中添加了一个Console.WriteLine()。秒表几乎精确测量了 60 秒,只有一条消息被写入Console。我在这里错过了什么吗?
【解决方案6】:

混淆了一个带有谓词的匿名函数。这是一个小辅助方法:

   WebDriverWait wait;
    private void waitForById(string id)
    {
        if (wait == null)
            wait = new WebDriverWait(driver, new TimeSpan(0, 0, 5));

        //wait.Until(driver);
        wait.Until(d => d.FindElement(By.Id(id)));
    }

【讨论】:

    【解决方案7】:

    您可以在 C# 中找到类似的内容。

    这是我在 JUnit 中使用的 - Selenium

    WebDriverWait wait = new WebDriverWait(driver, 100);
    WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("submit")));
    

    导入相关包。

    【讨论】:

    • 我今天尝试使用它,VS.net 给了我警告:OpenQA.Selenium.Support.UI.ExpectedConditions 类已被标记为“已弃用”并在@上“迁移到 DotNetSeleniumExtras”repo 987654322@
    【解决方案8】:
    public bool doesWebElementExist(string linkexist)
    {
         try
         {
            driver.FindElement(By.XPath(linkexist));
            return true;
         }
         catch (NoSuchElementException e)
         {
            return false;
         }
    }
    

    【讨论】:

    • 上面的代码是检查特定元素是否存在。
    【解决方案9】:
    // Wait up to 5 seconds with no minimum for a UI element to be found
    WebDriverWait wait = new WebDriverWait(_pagedriver, TimeSpan.FromSeconds(5));
    IWebElement title = wait.Until<IWebElement>((d) =>
    {
        return d.FindElement(By.ClassName("MainContentHeader"));
    });
    

    【讨论】:

      【解决方案10】:

      当您在 Selenium IDE 中选择 Webdriver 格式时,clickAndWait 命令不会被转换。这是解决方法。在下面添加等待行。实际上,问题是在我的 C# 代码中的这一行 1 之前发生的单击或事件。但实际上,只要确保在引用“By”对象的任何操作之前都有一个 WaitForElement。

      HTML 代码:

      <a href="http://www.google.com">xxxxx</a>
      

      C#/NUnit 代码:

      driver.FindElement(By.LinkText("z")).Click;
      driver.WaitForElement(By.LinkText("xxxxx"));
      driver.FindElement(By.LinkText("xxxxx")).Click();
      

      【讨论】:

        【解决方案11】:

        试试这个代码:

         New WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(Function(d) d.FindElement(By.Id("controlName")).Displayed)
        

        【讨论】:

        • 你应该解释你做了什么以及为什么这可以解决问题。请格式化您的代码。
        【解决方案12】:

        Python:

        from selenium import webdriver
        from selenium.webdriver.support import expected_conditions as EC
        from selenium.webdriver.support.ui import WebDriverWait
        from selenium.webdriver.common.by import By
        
        driver.find_element_by_id('someId').click()
        
        WebDriverWait(driver, timeout).until(EC.presence_of_element_located((By.ID, 'someAnotherId'))
        

        ECexpected_conditions的导入),你也可以选择其他条件。 试试这个:Expected conditions Support

        【讨论】:

        • 这个问题被标记为 C#,而不是 Python。这个答案是无关紧要的。
        【解决方案13】:

        显式等待

        public static  WebDriverWait wait = new WebDriverWait(driver, 60);
        

        例子:

        wait.until(ExpectedConditions.visibilityOfElementLocated(UiprofileCre.UiaddChangeUserLink));
        

        【讨论】:

        • 一个解释是有序的,特别是它与以前的答案有什么不同。
        【解决方案14】:

        您不想在元素更改之前等待太久。在这段代码中,webdriver 在继续之前最多等待 2 秒。

        WebDriverWait 等待 = 新 WebDriverWait(驱动程序,TimeSpan.FromMilliseconds(2000)); wait.Until(ExpectedConditions.VisibilityOfAllElementsLocatedBy(By.Name("html-name")));

        【讨论】:

          【解决方案15】:

          使用Rn222's answeraknuds1's answer 来使用返回单个元素或列表的ISearchContext。并且可以指定最少元素数量:

          public static class SearchContextExtensions
          {
              /// <summary>
              ///     Method that finds an element based on the search parameters within a specified timeout.
              /// </summary>
              /// <param name="context">The context where this is searched. Required for extension methods</param>
              /// <param name="by">The search parameters that are used to identify the element</param>
              /// <param name="timeOutInSeconds">The time that the tool should wait before throwing an exception</param>
              /// <returns> The first element found that matches the condition specified</returns>
              public static IWebElement FindElement(this ISearchContext context, By by, uint timeOutInSeconds)
              {
                  if (timeOutInSeconds > 0)
                  {
                      var wait = new DefaultWait<ISearchContext>(context);
                      wait.Timeout = TimeSpan.FromSeconds(timeOutInSeconds);
                      return wait.Until<IWebElement>(ctx => ctx.FindElement(by));
                  }
                  return context.FindElement(by);
              }
              /// <summary>
              ///     Method that finds a list of elements based on the search parameters within a specified timeout.
              /// </summary>
              /// <param name="context">The context where this is searched. Required for extension methods</param>
              /// <param name="by">The search parameters that are used to identify the element</param>
              /// <param name="timeoutInSeconds">The time that the tool should wait before throwing an exception</param>
              /// <returns>A list of all the web elements that match the condition specified</returns>
              public static IReadOnlyCollection<IWebElement> FindElements(this ISearchContext context, By by, uint timeoutInSeconds)
              {
          
                  if (timeoutInSeconds > 0)
                  {
                      var wait = new DefaultWait<ISearchContext>(context);
                      wait.Timeout = TimeSpan.FromSeconds(timeoutInSeconds);
                      return wait.Until<IReadOnlyCollection<IWebElement>>(ctx => ctx.FindElements(by));
                  }
                  return context.FindElements(by);
              }
              /// <summary>
              ///     Method that finds a list of elements with the minimum amount specified based on the search parameters within a specified timeout.<br/>
              /// </summary>
              /// <param name="context">The context where this is searched. Required for extension methods</param>
              /// <param name="by">The search parameters that are used to identify the element</param>
              /// <param name="timeoutInSeconds">The time that the tool should wait before throwing an exception</param>
              /// <param name="minNumberOfElements">
              ///     The minimum number of elements that should meet the criteria before returning the list <para/>
              ///     If this number is not met, an exception will be thrown and no elements will be returned
              ///     even if some did meet the criteria
              /// </param>
              /// <returns>A list of all the web elements that match the condition specified</returns>
              public static IReadOnlyCollection<IWebElement> FindElements(this ISearchContext context, By by, uint timeoutInSeconds, int minNumberOfElements)
              {
                  var wait = new DefaultWait<ISearchContext>(context);
                  if (timeoutInSeconds > 0)
                  {
                      wait.Timeout = TimeSpan.FromSeconds(timeoutInSeconds);
                  }
          
                  // Wait until the current context found the minimum number of elements. If not found after timeout, an exception is thrown
                  wait.Until<bool>(ctx => ctx.FindElements(by).Count >= minNumberOfElements);
          
                  // If the elements were successfuly found, just return the list
                  return context.FindElements(by);
              }
          
          }
          

          使用示例:

          var driver = new FirefoxDriver();
          driver.Navigate().GoToUrl("http://localhost");
          var main = driver.FindElement(By.Id("main"));
          // It can be now used to wait when using elements to search
          var btn = main.FindElement(By.Id("button"), 10);
          btn.Click();
          // This will wait up to 10 seconds until a button is found
          var button = driver.FindElement(By.TagName("button"), 10)
          // This will wait up to 10 seconds until a button is found, and return all the buttons found
          var buttonList = driver.FindElements(By.TagName("button"), 10)
          // This will wait for 10 seconds until we find at least 5 buttons
          var buttonsMin = driver.FindElements(By.TagName("button"), 10, 5);
          driver.Close();
          

          【讨论】:

          • 异常在:返回 wait.Until(ctx => ctx.FindElement(by));
          【解决方案16】:

          由于我使用已经找到的 IWebElement 来分隔页面元素定义和页面测试场景以实现可见性,因此可以这样完成:

          public static void WaitForElementToBecomeVisibleWithinTimeout(IWebDriver driver, IWebElement element, int timeout)
          {
              new WebDriverWait(driver, TimeSpan.FromSeconds(timeout)).Until(ElementIsVisible(element));
          }
          
          private static Func<IWebDriver, bool> ElementIsVisible(IWebElement element)
          {
              return driver => {
                  try
                  {
                      return element.Displayed;
                  }
                  catch(Exception)
                  {
                      // If element is null, stale or if it cannot be located
                      return false;
                  }
              };
          }
          

          【讨论】:

            【解决方案17】:

            这是使用显式等待来等待DOM中存在的元素的可重用函数。

            public void WaitForElement(IWebElement element, int timeout = 2)
            {
                WebDriverWait wait = new WebDriverWait(webDriver, TimeSpan.FromMinutes(timeout));
                wait.IgnoreExceptionTypes(typeof(NoSuchElementException));
                wait.IgnoreExceptionTypes(typeof(StaleElementReferenceException));
                wait.Until<bool>(driver =>
                {
                    try
                    {
                        return element.Displayed;
                    }
                    catch (Exception)
                    {
                        return false;
                    }
                });
            }
            

            【讨论】:

            • 欢迎来到 Stack Overflow,请勿发布纯代码答案。
            【解决方案18】:

            我们可以这样实现:

            public static IWebElement WaitForObject(IWebDriver DriverObj, By by, int TimeOut = 30)
            {
                try
                {
                    WebDriverWait Wait1 = new WebDriverWait(DriverObj, TimeSpan.FromSeconds(TimeOut));
                    var WaitS = Wait1.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.PresenceOfAllElementsLocatedBy(by));
                    return WaitS[0];
                }
                catch (NoSuchElementException)
                {
                    Reports.TestStep("Wait for Element(s) with xPath was failed in current context page.");
                    throw;
                }
            }
            

            【讨论】:

            • 与之前的答案有何不同?
            【解决方案19】:

            你可以使用下面的

            WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0,0,5));
            wait.Until(ExpectedConditions.ElementToBeClickable((By.Id("login")));
            

            【讨论】:

            • 一个解释是有序的,特别是它与以前的答案有什么不同。
            【解决方案20】:

            我看到已经发布了多个效果很好的解决方案!但是,以防万一有人需要其他东西,我想我会发布两个我个人在 Selenium C# 中使用的解决方案来测试元素是否存在!

            public static class IsPresent
            {
                public static bool isPresent(this IWebDriver driver, By bylocator)
                {
            
                    bool variable = false;
                    try
                    {
                        IWebElement element = driver.FindElement(bylocator);
                        variable = element != null;
                    }
                    catch (NoSuchElementException){
            
                    }
                    return variable;
                }
            }
            

            这是第二个:

            public static class IsPresent2
            {
                public static bool isPresent2(this IWebDriver driver, By bylocator)
                {
                    bool variable = true;
                    try
                    {
                        IWebElement element = driver.FindElement(bylocator);
                    }
                    catch (NoSuchElementException)
                    {
                        variable = false;
                    }
                    return variable;
                }
            }
            

            【讨论】:

              【解决方案21】:

              WebDriverWait 不会生效。

              var driver = new FirefoxDriver(
                  new FirefoxOptions().PageLoadStrategy = PageLoadStrategy.Eager
              );
              driver.Navigate().GoToUrl("xxx");
              new WebDriverWait(driver, TimeSpan.FromSeconds(60))
                  .Until(d => d.FindElement(By.Id("xxx"))); // A tag that close to the end
              

              一旦页面“交互式”,这将立即引发异常。我不知道为什么,但是超时就像它不存在一样。

              也许SeleniumExtras.WaitHelpers 有效,但我没有尝试。它是官方的,但它被拆分为另一个 NuGet 包。您可以参考C# Selenium 'ExpectedConditions is obsolete'

              我使用FindElements 并检查Count == 0。如果为真,请使用await Task.Delay。确实效率不高。

              【讨论】:

              • 不用赋值使用'new'是什么意思?
              • 这只是一个测试。我认为它与OP的代码相同。
              【解决方案22】:

              使用C#扩展方法:我们可以解决等待元素可见的问题。
              特定元素的最大 reties 为 100。

              public static bool WaitForElementToBeVisible(IWebDriver browser, By by)
                      {
                          int attemptToFindElement = 0;
                          bool elementFound = false;
                          IWebElement elementIdentifier = null;
                          do
                          {
                              attemptToFindElement++;
                              try
                              {
                                  elementIdentifier = browser.FindWebElement(by);
                                  elementFound = (elementIdentifier.Displayed && elementIdentifier.Enabled) ? true : false;
                              }
                              catch (Exception)
                              {
                                  elementFound = false;
                              }
              
                          }
                          while (elementFound == false && attemptToFindElement < 100);
              
                          return elementFound;
                      }
              

              【讨论】:

                【解决方案23】:

                我正在使用它并且效果很好:

                public static bool elexists(By by, WebDriver driver)
                    {
                        try
                        {
                            driver.FindElement(by);
                            return true;
                        }
                        catch (NoSuchElementException)
                        {
                            return false;
                        }
                    }
                    public static void waitforelement(WebDriver driver, By by)
                    {
                        for (int i = 0; i < 30; i++)
                        {
                            System.Threading.Thread.Sleep(1000);
                            if (elexists(by, driver))
                            {
                                break;
                            }
                
                
                        }
                
                    }
                

                当然你可以添加超过 30 次尝试,并将检查周期缩短到 1 秒以内。

                用法:

                waitforelement(driver, By.Id("login"));
                IWebElement login= driver.FindElement(By.Id("login"));
                login.Click();
                

                【讨论】:

                  【解决方案24】:
                   new WebDriverWait(driver, TimeSpan.FromSeconds(10)).
                     Until(ExpectedConditions.PresenceOfAllElementsLocatedBy((By.Id("toast-container"))));
                  

                  【讨论】:

                  • ExpectedConditions 已弃用
                  • 解释一下。
                  【解决方案25】:

                  第一个答案很好,但我的问题是未处理的异常没有正确关闭网络驱动程序,它保持了我使用的第一个值,即 1 秒。

                  如果您遇到同样的问题,重新启动 Visual Studio 并确保正确处理所有异常

                  【讨论】:

                  • 现在您应该知道 Stack Overflow 中的答案没有排序,因此没有“第一个答案”
                  【解决方案26】:

                  这是在 Selenium 中等待条件的方法:

                      WebDriverWait wait = new WebDriverWait(m_driver, TimeSpan.FromSeconds(10));
                      wait.Until(d => ReadCell(row, col) != "");
                  

                  ReadCell(row, col) != "" 可以是任何条件。喜欢这种方式是因为:

                  • 这是我的
                  • 允许内联

                  【讨论】:

                    猜你喜欢
                    • 2015-08-29
                    • 2014-11-03
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-08-09
                    • 1970-01-01
                    • 2012-03-26
                    • 2014-05-29
                    相关资源
                    最近更新 更多