Selenium c # Webdriver: Poczekaj, aż element będzie obecny


185

Chcę się upewnić, że element jest obecny, zanim webdriver zacznie coś robić.

Staram się, aby coś takiego działało:

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

Głównie mam problem z konfiguracją funkcji anynomicznej.


3
Do Twojej wiadomości - budowanie takiego przedziału czasowego jest czystsze TimeSpan.FromSeconds(5). To sprawia, że ​​jest bardziej jasne IMO
Kolob Canyon

Odpowiedzi:


159

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.


5
dzięki, nowa składnia to: driver.manage (). timeouts (). implicitlyWait (10, TimeUnit.SECONDS);
Reda

20
@RedaBalkouch, składnia użyta przez Mike'a w odpowiedzi jest poprawna. Jest C #
Diemo

3
Jeśli zdecydujesz się na użycie niejawnych oczekiwań, uważaj, aby nie używać jawnych oczekiwań. Może to spowodować pewne nieprzewidywalne zachowanie prowadzące do złych wyników testu. Ogólnie rzecz biorąc, zalecałbym używanie jawnych oczekiwań zamiast niejawnych.
mrfreester,

7
Ta metoda jest teraz przestarzała, zamiast tego należy użyć właściwości ImplicitWait:Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
Samuel Rondeau-Millaire

1
Wykorzystałem dostarczone podejście i stwierdziłem, że metoda ta jest przestarzała, jak zauważył Samuel. Sprawdzanie istnienia elementu czeka teraz do określonego czasu.
Jim Scott

279

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

113
Na wypadek, gdyby ktoś się zastanawiał, WebDriverWaitpochodzi z OpenQA.Selenium.Support.UIprzestrzeni nazw i jest dostarczany w osobnym pakiecie o nazwie Selenium WebDriver Support ClassesNuGet
Andy

5
@ Ved mógłbym cię pocałować <3 szukałem go w innym dll: D
Adween

1
@Loudenvier Proszę pogrubić pierwszą linię, aby była bardziej zauważalna. Zwłaszcza, że ​​nie jest to akceptowana odpowiedź, chociaż jest to lepsze i bardziej precyzyjne podejście.
Rick,

5
Selenium WebDriver Support Classespojawia się teraz na NuGet jako „Selenium.Support” , aktualna wersja to 3.4.0
Eric F.,

1
Nadal miałem wiele błędów, dopóki nie użyłem tej linii 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.
poszukiwacz

84

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

Źródło


1
Uzgodnione, jest to o wiele bardziej przydatne niż zwykłe przekroczenie limitu czasu (w przypadku dynamicznego ładowania obiektu).
keithl8041

5
Chociaż to działa. Jest teraz oznaczony jako przestarzały, więc należy go unikać.
Adam Garner

3
Oto nowe podejście (nieaktualne): stackoverflow.com/a/49867605/331281
Dejan

1
Zauważ, że w tej chwili 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.
urig

30

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

7
Miły! Właśnie dodałem to do własnej biblioteki! To właśnie piękno dzielenia się kodem !!!
Loudenvier,

1
Sugerowałbym jeden dodatek do tego. Możesz złapać rozwiązanie NoSuchElement i zwrócić wartość null w tym przypadku. Następnie możesz utworzyć metodę rozszerzenia o nazwie .exists, która zwraca true, chyba że IWebElement ma wartość null.
Brantley Blanchard

17

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

Przykładowe użycie:

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
Jeśli ustawisz niejawne oczekiwanie, takie jak _webDriver.Manage().Timeouts().ImplicitlyWait(Timeout);to, będzie nadal atutem ustawioną tutaj wartość limitu czasu.
howcheng

To chyba nie działa dla mnie ...? Dodałem Stopwatchwokół 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?
urig

10

Pomyliłem dowolną funkcję z predykatem. Oto mała metoda pomocnicza:

   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

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


1
Próbowałem tego dzisiaj użyć, a VS.net daje mi ostrzeżenia: klasa OpenQA.Selenium.Support.UI.ExpectedConditions została oznaczona jako „przestarzała” i została „migrowana do repozytorium DotNetSeleniumExtras” na github.com/DotNetSeleniumTools
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;
     }
}

Powyższy kod ma na celu sprawdzenie, czy dany element jest obecny, czy nie.
Madhu

2

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

2

Pyton:

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


To pytanie jest oznaczone jako C #, a nie Python. Ta odpowiedź jest nieistotna.
Użytkownik

2

Wypróbuj ten kod:

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

4
Powinieneś wyjaśnić, co zrobiłeś i dlaczego to rozwiązuje problem. I proszę sformatować kod.
śpiewa

1

Jawne oczekiwanie

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

Przykład:

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

1

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

1

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


1

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

1

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

Witamy w stosie przepełnienia, nie publikuj odpowiedzi zawierających tylko kod.
JJ dla Transparency and Monica

0

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

0

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.WaitHelpersdział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 FindElementsi sprawdzam, czy Count == 0, jeśli to prawda, używam await Task.Delay. To naprawdę nie jest całkiem wydajne.


0

Możesz użyć następujących

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

-1

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

}

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

Oczekiwane warunki są przestarzałe
GELR

-1

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 studioi upewnij się, że all the exceptions are handledodpowiednio.


Do tej pory powinieneś wiedzieć, że w przepełnieniu stosu nie ma kolejności odpowiedzi, więc nie ma „pierwszej odpowiedzi”
Antti Haapala

-2

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ż:

  • To jest moje
  • umożliwia inlining
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.