Selenium c#Webdriver:等待直到元素存在


185

我想确保在webdriver开始做事之前存在一个元素。

我正在尝试使类似的东西起作用:

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

我主要是在努力设置任意函数。


3
仅供参考-这样建立您的时间跨度比较干净TimeSpan.FromSeconds(5)。海事组织(IMO)
Kolob Canyon

Answers:


159

另外,您可以使用隐式等待:

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

隐式等待是告诉WebDriver在尝试查找不立即可用的一个或多个元素时,在一定时间内轮询DOM。默认设置为0。设置后,将在WebDriver对象实例的生存期内设置隐式等待。


5
谢谢,新语法是:driver.manage()。timeouts()。implicitlyWait(10,TimeUnit.SECONDS);
Reda 2013年

20
@ RedaBalkouch,Mike在他的答案中使用的语法是正确的。这是C#
Diemo 2014年

3
如果选择使用隐式等待,请注意不要使用显式等待。这可能会导致某些不可预测的行为,从而导致不良的测试结果。一般来说,我建议使用显式等待而不是隐式等待。
mrfreester '16

7
现在不推荐使用此方法,而应使用属性ImplicitWait:Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
Samuel Rondeau-Millaire

1
我使用了所提供的方法,发现该方法已被Samuel指出不推荐使用。现在检查项目是否存在将等待指定的时间。
吉姆·斯科特

279

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

113
如果有人想知道,WebDriverWait它们来自OpenQA.Selenium.Support.UI命名空间,并放在一个名为Selenium WebDriver Support ClassesNuGet 的单独程序包中
Andy

5
@Ved我可以吻你<3一直在寻找它在不同的dll:d
Adween

1
@Loudenvier请使第一行加粗,以使其更引人注意。尽管这是一种更好,更精确的方法,但由于它不是公认的答案,因此尤其如此。
里克(Rick)

5
Selenium WebDriver Support Classes现在在NuGet上显示为“ Selenium.Support”,当前版本为3.4.0
EricF。

1
在使用此行之前,我仍然有很多错误,return wait.Until(ExpectedConditions.ElementToBeClickable(by));并且现在效果很好。抬起头,以防其他人得到仍然找不到的随机元素。
勘探者

84

您也可以使用

ExpectedConditions.ElementExists

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

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

资源


1
同意,这比仅超时(在动态加载对象的情况下)有用得多。
keithl8041 2014年

5
虽然这有效。现在已将其标记为已弃用,因此应避免使用。
亚当·加纳

3
这是新方法(不建议使用):stackoverflow.com/a/49867605/331281
Dejan

1
请注意,此时,“ DotNetSeleniumExtras.WaitHelpers(未由@Dejan引用)”“未维护,问题不会得到解决,PR将不被接受”。(来源:github.com/SeleniumHQ/selenium/issues/…)。它的发行商正在寻找维护者以从他那里接管它。
urig

30

这是@Loudenvier解决方案的一种变体,也可以用于获取多个元素:

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

7
真好!我刚刚将其添加到我自己的库中!那就是共享代码的美!
Loudenvier 2012年

1
我建议添加一个。您可以捕获NoSuchElement解决方案,并在该实例中返回null。然后,您可以创建一个名为.exists的扩展方法,除非IWebElement为null,否则该方法将返回true。
布兰特利·布兰查德

17

受Loudenvier解决方案的启发,以下是一种扩展方法,该方法适用于所有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();

1
如果您设置了这样的隐式等待,_webDriver.Manage().Timeouts().ImplicitlyWait(Timeout);那将仍然胜过您在此处设置的超时值。
howcheng 2015年

这似乎对我不起作用...?我Stopwatch在扩展方法周围添加了一个调用,并Console.WriteLine()在发送给的lambda内部添加了一个Until()。秒表几乎准确地计时了60秒,仅写了一条信息Console。我在这里想念什么吗?
urig

10

我把谓词混淆了所有函数。这是一个小帮手方法:

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

5

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

这就是我在JUnit中使用的-Selenium

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

导入相关包


1
我今天尝试使用此工具,VS.net向我发出警告:OpenQA.Selenium.Support.UI.ExpectedConditions类已被标记为“已弃用”,并已在github.com/DotNetSeleniumTools上“迁移到DotNetSeleniumExtras” 仓库
Jeff Mergler

3
//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"));
});

3
public bool doesWebElementExist(string linkexist)
{
     try
     {
        driver.FindElement(By.XPath(linkexist));
        return true;
     }
     catch (NoSuchElementException e)
     {
        return false;
     }
}

上面的代码用于检查特定元素是否存在。
Madhu

2

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

HTML代码:

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

C#/ N单元代码:

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

2

蟒蛇:

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'))

在EC中,您还可以选择其他条件,也可以尝试以下方法:http : //selenium-python.readthedocs.org/api.html#module-selenium.webdriver.support.expected_conditions


这个问题被标记为C#,而不是Python。这个答案无关紧要。
用户

2

试试这个代码:

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

4
您应该说明自己做了什么以及为什么可以解决问题。并请格式化您的代码。
hering

1

显式等待

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

例:

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

1

使用Rn222和Aknuds1来使用ISearchContext,该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();

1

您不想在元素更改之前等待太久。在此代码中,网络驱动程序最多等待2秒钟才能继续。

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


1

由于我使用已经发现的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;
        }
    };
}

1

这是可重用的函数,可以使用显式等待来等待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,请不要发布仅代码的答案。
JJ for Transparency和Monica

0

我们可以这样实现:

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

0

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已过时'

我自己正在使用,FindElements并检查是否为Count == 0true await Task.Delay。确实效率不高。


0

您可以使用以下内容

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

-1

我看到已经发布了多种解决方案,效果很好!但是,以防万一有人需要其他东西,我想我会发布两个我自己在硒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; 
    }

}

-1
 new WebDriverWait(driver, TimeSpan.FromSeconds(10)).
   Until(ExpectedConditions.PresenceOfAllElementsLocatedBy((By.Id("toast-container"))));

ExpectedConditions已弃用
GELR,

-1

第一个答案是好的,我的问题是未处理的异常无法正确关闭Web驱动程序并且它保持与我使用的相同的第一个值(即1秒)。

如果遇到同样的问题

restart you visual studio并确保all the exceptions are handled正确。


现在,您应该知道堆栈溢出中没有排序的答案,因此没有“第一答案”
Antti Haapala

-2

正在搜索如何在Selenium中等待条件,进入此线程,这是我现在使用的方法:

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

ReadCell(row, col) != ""可以是任何条件。之所以这样,是因为:

  • 这是我的
  • 允许内联
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.