Selenium c# WebDriver:等待元素出现

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

我正在尝试这样做:

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

我主要是挣扎如何设置匿名功能…

439173 次浏览

I 困惑是一个带谓词的匿名函数。这里有一个小帮手方法:

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

你也可以使用隐式等待:

driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
一个隐式的等待是告诉WebDriver轮询DOM 在试图找到一个或多个元素时的时间 暂时不可用。默认设置为0。设置好后, 在WebDriver对象实例的生命周期中设置隐式等待
// 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"));
});

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

下面是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);
}
}

我看到许多解决方案已经张贴,工作很棒!但是,为了防止有人需要其他东西,我想我将发布两个我个人在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;
}
}

你可以在c#中找到类似的东西。

这是我在JUnit - Selenium中使用的

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

导入相关的包。

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

下面是如何在Selenium中等待一个条件:

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

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

  • 它是我的
  • 允许内联

你也可以使用

ExpectedConditions。ElementExists

你会搜索这样的元素可用性

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

Source

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

电子商务(导入expected_conditions),你也可以选择其他条件。 试试这个:预期条件支持

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

显式等

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

例子:

wait.until(ExpectedConditions.visibilityOfElementLocated(UiprofileCre.UiaddChangeUserLink));
 new WebDriverWait(driver, TimeSpan.FromSeconds(10)).
Until(ExpectedConditions.PresenceOfAllElementsLocatedBy((By.Id("toast-container"))));

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

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

您不希望在元素更改之前等待太长时间。在这段代码中,webdriver在继续之前最多等待2秒。



WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromMilliseconds(2000));
wait.Until(ExpectedConditions.VisibilityOfAllElementsLocatedBy(By.Name("html-name")));


试试下面的代码:

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

使用Rn222的回答aknuds1的回答来使用返回单个元素或列表的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();

因为我使用一个已经找到的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;
}
};
}

这是一个可重用的函数,用于等待使用显式等在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;
}
});
}

我们可以这样实现:

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

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

我使用FindElements并检查Count == 0。如果为真,使用await Task.Delay。这真的不是很有效。

你可以使用下面的方法

WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0,0,5));
wait.Until(ExpectedConditions.ElementToBeClickable((By.Id("login")));
< p > 使用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;
}

我正在用这个,效果很好:

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

你可以使用以下方法:

使用命名空间:

using SeleniumExtras.WaitHelpers;

在代码:

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