Odpowiedzi:
Alternatywnie możesz użyć niejawnego czekania:
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
Domniemane oczekiwanie polega na tym, aby WebDriver odpytywał DOM przez pewien czas podczas próby znalezienia elementu lub elementów, jeśli nie są one natychmiast dostępne. Ustawieniem domyślnym jest 0. Po ustawieniu domyślny czas oczekiwania jest ustawiany na czas życia instancji obiektu WebDriver.
Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
Korzystanie z rozwiązania dostarczonego przez Mike'a Kwana może mieć wpływ na ogólną wydajność testowania, ponieważ niejawne oczekiwanie zostanie wykorzystane we wszystkich wywołaniach FindElement. Wiele razy chcesz, aby FindElement od razu zawiódł, gdy element nie jest obecny (testujesz pod kątem zniekształconej strony, brakujących elementów itp.). Z niejawnym oczekiwaniem operacje te czekałyby na wygaśnięcie limitu czasu przed zgłoszeniem wyjątku. Domyślne niejawne oczekiwanie jest ustawione na 0 sekund.
Napisałem małą metodę rozszerzenia do IWebDriver, która dodaje parametr limitu czasu (w sekundach) do FindElement()
metody. Jest to dość oczywiste:
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);
}
}
Nie buforowałem obiektu WebDriverWait, ponieważ jego tworzenie jest bardzo tanie, to rozszerzenie może być używane jednocześnie dla różnych obiektów WebDriver, a optymalizacje wykonuję tylko wtedy, gdy jest to ostatecznie potrzebne.
Użycie jest proste:
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
pochodzi z OpenQA.Selenium.Support.UI
przestrzeni nazw i jest dostarczany w osobnym pakiecie o nazwie Selenium WebDriver Support Classes
NuGet
Selenium WebDriver Support Classes
pojawia się teraz na NuGet jako „Selenium.Support” , aktualna wersja to 3.4.0
return wait.Until(ExpectedConditions.ElementToBeClickable(by));
i teraz działa świetnie. Heads up, na wypadek, gdyby ktokolwiek dostał losowe elementy, których nadal nie znaleziono.
Możesz także użyć
ExpectedConditions.ElementExists
Będziesz więc szukać takiej dostępności elementu
new WebDriverWait(driver, TimeSpan.FromSeconds(timeOut)).Until(ExpectedConditions.ElementExists((By.Id(login))));
DotNetSeleniumExtras.WaitHelpers
(o którym mowa powyżej przez @Dejana) „nie jest utrzymywany, problemy nie zostaną rozwiązane, PR nie będą akceptowane”. (źródło: github.com/SeleniumHQ/selenium/issues/… ). Wydawca szuka opiekuna, który go przejmie.
Oto odmiana rozwiązania @ Loudenvier, która działa również w celu uzyskania wielu elementów:
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);
}
}
Zainspirowany rozwiązaniem Loudenviera, oto metoda rozszerzenia, która działa dla wszystkich obiektów ISearchContext, nie tylko IWebDriver, która jest specjalizacją tego pierwszego. Ta metoda obsługuje również oczekiwanie na wyświetlenie elementu.
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);
to, będzie nadal atutem ustawioną tutaj wartość limitu czasu.
Stopwatch
wokół wywołania metodę rozszerzenia i Console.WriteLine()
wewnątrz przesłanej lambdy Until()
. Stoper mierzył prawie dokładnie 60 sekund i zapisano tylko jedną wiadomość Console
. Czy coś mi umyka?
Możesz dowiedzieć się czegoś takiego w C #.
Tego właśnie użyłem w JUnit - Selenium
WebDriverWait wait = new WebDriverWait(driver, 100);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("submit")));
Wykonaj import powiązanych pakietów
//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"));
});
Polecenie clickAndWait nie jest konwertowane po wybraniu formatu Webdriver w Selenium IDE. Oto obejście. Dodaj wiersz oczekiwania poniżej. Realistycznie problem polegał na kliknięciu lub zdarzeniu, które miało miejsce przed tym wierszem 1 w moim kodzie C #. Ale tak naprawdę, po prostu upewnij się, że masz WaitForElement przed jakąkolwiek akcją, do której odwołujesz się do obiektu „Według”.
Kod HTML:
<a href="http://www.google.com">xxxxx</a>
Kod C # / NUnit:
driver.FindElement(By.LinkText("z")).Click;
driver.WaitForElement(By.LinkText("xxxxx"));
driver.FindElement(By.LinkText("xxxxx")).Click();
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'))
z EC możesz wybrać inne warunki, a także wypróbować: http://selenium-python.readthedocs.org/api.html#module-selenium.webdriver.support.expected_conditions
Wypróbuj ten kod:
New WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(Function(d) d.FindElement(By.Id("controlName")).Displayed)
Użyto Rn222 i Aknuds1, aby użyć ISearchContext, który zwraca pojedynczy element lub listę. I można określić minimalną liczbę elementów:
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);
}
}
Przykładowe użycie:
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();
Nie chcesz długo czekać na zmianę elementu. W tym kodzie webdriver czeka do 2 sekund, zanim będzie kontynuowany.
WebDriverWait wait = nowy WebDriverWait (sterownik, TimeSpan.FromMilliseconds (2000)); wait.Until (ExpectedConditions.VisibilityOfAllElementsLocatedBy (By.Name („nazwa HTML”)));
Ponieważ rozdzielam definicje elementów strony i scenariusze testowania strony, używając już znalezionego IWebElement dla widoczności, można to zrobić w następujący sposób:
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;
}
};
}
Jest to funkcja wielokrotnego użytku do oczekiwania na element obecny w DOM przy użyciu Explicit Wait.
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;
}
});
}
Możemy to osiągnąć w następujący sposób:
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
nie zadziała.
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
Spowodowałoby to natychmiastowy wyjątek, gdy strona jest „interaktywna”. Nie wiem dlaczego, ale limit czasu działa tak, jakby nie istniał.
Być może SeleniumExtras.WaitHelpers
działa, ale nie próbowałem. Jest to oficjalne, ale zostało podzielone na inny pakiet nuget. Możesz odwołać się do C # Selenium „Oczekiwane warunki są przestarzałe” .
Ja używam FindElements
i sprawdzam, czy Count == 0
, jeśli to prawda, używam await Task.Delay
. To naprawdę nie jest całkiem wydajne.
Możesz użyć następujących
WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0,0,5));
wait.Until(ExpectedConditions.ElementToBeClickable((By.Id("login")));
Widzę już wiele opublikowanych rozwiązań, które działają świetnie! Jednak na wypadek, gdyby ktoś potrzebował czegoś innego, pomyślałem, że opublikuję dwa rozwiązania, których osobiście użyłem w selenie C #, aby sprawdzić, czy element jest obecny! Mam nadzieję, że to pomaga, na zdrowie!
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;
}
}
Oto drugi
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;
}
}
Pierwsza odpowiedź jest dobra, moim problemem było to, że nieobsługiwane wyjątki nie zamykały poprawnie sterownika sieciowego i zachowywały tę samą pierwszą wartość, której użyłem, czyli 1 sekundę.
Jeśli wystąpi ten sam problem
restart you visual studio
i upewnij się, że all the exceptions are handled
odpowiednio.
Szukałem, jak czekać w Selenium na stan, wylądowałem w tym wątku i oto, czego teraz używam:
WebDriverWait wait = new WebDriverWait(m_driver, TimeSpan.FromSeconds(10));
wait.Until(d => ReadCell(row, col) != "");
ReadCell(row, col) != ""
może być dowolny warunek. Polub ten sposób, ponieważ:
TimeSpan.FromSeconds(5)
. To sprawia, że jest bardziej jasne IMO