Selenium c # Webdriver: Warten Sie, bis das Element vorhanden ist

185

Ich möchte sicherstellen, dass ein Element vorhanden ist, bevor der Webdriver anfängt, Dinge zu tun.

Ich versuche so etwas zum Laufen zu bringen:

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

Ich habe hauptsächlich Probleme damit, die beliebige Funktion einzurichten.

AyKarsi
quelle
3
Zu Ihrer Information - es ist sauberer, Ihre Zeitspanne so aufzubauen TimeSpan.FromSeconds(5). Es macht es klarer IMO
Kolob Canyon

Antworten:

159

Alternativ können Sie implizites Warten verwenden:

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

Eine implizite Wartezeit besteht darin, WebDriver anzuweisen, das DOM für eine bestimmte Zeitspanne abzufragen, wenn versucht wird, ein Element oder Elemente zu finden, wenn diese nicht sofort verfügbar sind. Die Standardeinstellung ist 0. Nach dem Festlegen wird die implizite Wartezeit für die Lebensdauer der WebDriver-Objektinstanz festgelegt.

Mike Kwan
quelle
5
danke, die neue Syntax lautet: driver.manage (). timeouts (). implicitlyWait (10, TimeUnit.SECONDS);
Reda
20
@RedaBalkouch, die Syntax, die Mike in seiner Antwort verwendet hat, ist korrekt. Es ist C #
Diemo
3
Wenn Sie implizite Wartezeiten verwenden, achten Sie darauf, keine expliziten Wartezeiten zu verwenden. Dies kann zu unvorhersehbarem Verhalten führen und zu schlechten Testergebnissen führen. Im Allgemeinen würde ich empfehlen, explizite Wartezeiten gegenüber impliziten Wartezeiten zu verwenden.
Herr Freester
7
Diese Methode ist jetzt veraltet, Sie sollten stattdessen die Eigenschaft ImplicitWait verwenden:Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
Samuel Rondeau-Millaire
1
Ich habe den angegebenen Ansatz verwendet und festgestellt, dass die Methode veraltet ist, wie von Samuel hervorgehoben. Die Überprüfung auf das Vorhandensein eines Elements wartet nun bis zur angegebenen Zeit.
Jim Scott
279

Die Verwendung der von Mike Kwan bereitgestellten Lösung kann sich auf die Gesamttestleistung auswirken, da das implizite Warten in allen FindElement-Aufrufen verwendet wird. Oft möchten Sie, dass das FindElement sofort fehlschlägt, wenn ein Element nicht vorhanden ist (Sie testen auf eine fehlerhafte Seite, fehlende Elemente usw.). Mit dem impliziten Warten würden diese Operationen warten, bis das gesamte Zeitlimit abgelaufen ist, bevor die Ausnahme ausgelöst wird. Die implizite Standardwartezeit ist auf 0 Sekunden festgelegt.

Ich habe eine kleine Erweiterungsmethode für IWebDriver geschrieben, die der FindElement()Methode einen Timeout-Parameter (in Sekunden) hinzufügt . Es ist ziemlich selbsterklärend:

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

Ich habe das WebDriverWait-Objekt nicht zwischengespeichert, da seine Erstellung sehr billig ist. Diese Erweiterung kann gleichzeitig für verschiedene WebDriver-Objekte verwendet werden, und ich mache Optimierungen nur, wenn sie letztendlich benötigt werden.

Die Verwendung ist unkompliziert:

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();
Loudenvier
quelle
113
Für den Fall, dass sich jemand wundert, WebDriverWaitsind aus dem OpenQA.Selenium.Support.UINamespace und kommt in einem separaten Paket namens Selenium WebDriver Support ClassesNuGet
Andy
5
@Ved ich könnte dich küssen <3 habe in einer anderen DLL danach
gesucht
1
@Loudenvier Bitte machen Sie die erste Zeile fett, damit sie besser wahrgenommen wird. Zumal es nicht die akzeptierte Antwort ist, obwohl es ein besserer und präziserer Ansatz ist.
Rick
5
Selenium WebDriver Support Classesist jetzt auf NuGet als "Selenium.Support" erschienen , aktuelle Version ist 3.4.0
Eric F.
1
Ich hatte immer noch viele Fehler, bis ich diese Leitung benutzte return wait.Until(ExpectedConditions.ElementToBeClickable(by));und sie funktioniert jetzt großartig. Heads up für den Fall, dass jemand andere zufällige Elemente erhält, die noch nicht gefunden wurden.
Prospektor
84

Sie können auch verwenden

ExpectedConditions.ElementExists

Sie werden also nach einer solchen Elementverfügbarkeit suchen

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

Quelle

Zain Ali
quelle
1
Einverstanden ist dies weitaus nützlicher als eine bloße Zeitüberschreitung (in Fällen, in denen Sie ein Objekt dynamisch laden).
keithl8041
5
Während das funktioniert. Es ist jetzt als veraltet markiert und sollte daher vermieden werden.
Adam Garner
3
Hier ist der neue Ansatz (nicht veraltet): stackoverflow.com/a/49867605/331281
Dejan
1
Beachten Sie, dass zu diesem Zeitpunkt die DotNetSeleniumExtras.WaitHelpers(von @Dejan oben genannte) "nicht beibehalten wird, Probleme nicht behoben werden, PRs nicht akzeptiert werden". (Quelle: github.com/SeleniumHQ/selenium/issues/… ). Sein Verlag sucht einen Betreuer, der es von ihm übernimmt.
Urig
30

Hier ist eine Variation der @ Loudenvier-Lösung, mit der auch mehrere Elemente abgerufen werden können:

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);
    }
}
Rn222
quelle
7
Nett! Ich habe dies gerade zu meiner eigenen Bibliothek hinzugefügt! Das ist das Schöne am Teilen von Code !!!
Loudenvier
1
Ich würde eine Ergänzung dazu vorschlagen. Sie könnten die NoSuchElement-Lösung abfangen und in diesem Fall null zurückgeben. Anschließend können Sie eine Erweiterungsmethode mit dem Namen .exists erstellen, die true zurückgibt, sofern das IWebElement nicht null ist.
Brantley Blanchard
17

Inspiriert von Loudenviers Lösung, ist hier eine Erweiterungsmethode, die für alle ISearchContext-Objekte funktioniert, nicht nur für IWebDriver, eine Spezialisierung der ersteren. Diese Methode unterstützt auch das Warten, bis das Element angezeigt wird.

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

Anwendungsbeispiel:

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();
aknuds1
quelle
1
Wenn Sie eine implizite Wartezeit wie diese festgelegt haben _webDriver.Manage().Timeouts().ImplicitlyWait(Timeout);, wird der hier festgelegte Zeitlimitwert immer noch übertroffen.
Howcheng
Das scheint bei mir nicht zu funktionieren ...? Ich habe Stopwatchder Erweiterungsmethode einen Console.WriteLine()Rundum- Aufruf hinzugefügt und einen innerhalb des Lambda an gesendet Until(). Die Stoppuhr maß fast genau 60 Sekunden und es wurde nur eine Nachricht geschrieben Console. Vermisse ich hier etwas?
Urig
10

Ich habe jede Funktion mit Prädikat verwechselt. Hier ist eine kleine Hilfsmethode:

   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)));
    }
AyKarsi
quelle
5

Sie können so etwas in C # herausfinden.

Das habe ich in JUnit - Selenium verwendet

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

Importieren Sie verwandte Pakete

Aditi
quelle
1
Ich habe heute versucht, dies zu verwenden, und VS.net gibt mir Warnungen: Die OpenQA.Selenium.Support.UI.ExpectedConditions-Klasse wurde als "veraltet" markiert und auf 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"));
});
Brian121212
quelle
3
public bool doesWebElementExist(string linkexist)
{
     try
     {
        driver.FindElement(By.XPath(linkexist));
        return true;
     }
     catch (NoSuchElementException e)
     {
        return false;
     }
}
Madhu
quelle
Mit dem obigen Code soll überprüft werden, ob ein bestimmtes Element vorhanden ist oder nicht.
Madhu
2

Der Befehl clickAndWait wird nicht konvertiert, wenn Sie das Webdriver-Format in der Selenium-IDE auswählen. Hier ist die Problemumgehung. Fügen Sie die Warteschlange unten hinzu. Realistisch gesehen war das Problem das Klicken oder Ereignis, das vor dieser einzeiligen Zeile 1 in meinem C # -Code aufgetreten ist. Stellen Sie jedoch vor jeder Aktion, bei der Sie auf ein "By" -Objekt verweisen, sicher, dass Sie über ein WaitForElement verfügen.

HTML Quelltext:

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

C # / NUnit-Code:

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

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

Bei EC können Sie auch andere Bedingungen auswählen: http://selenium-python.readthedocs.org/api.html#module-selenium.webdriver.support.expected_conditions

Md. Nazmul Haque Sarker
quelle
Diese Frage ist mit C # gekennzeichnet, nicht mit Python. Diese Antwort ist irrelevant.
Benutzer
2

Versuchen Sie diesen Code:

 New WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(Function(d) d.FindElement(By.Id("controlName")).Displayed)
Ammar Ben Hadj Amor
quelle
4
Sie sollten erklären, was Sie getan haben und warum dies das Problem löst. Und bitte formatieren Sie Ihren Code.
Hering
1

Explizites Warten

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

Beispiel:

wait.until(ExpectedConditions.visibilityOfElementLocated(UiprofileCre.UiaddChangeUserLink));
Pavan T.
quelle
1

Verwendete Rn222 und Aknuds1, um einen ISearchContext zu verwenden, der entweder ein einzelnes Element oder eine Liste zurückgibt. Und eine Mindestanzahl von Elementen kann angegeben werden:

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

}

Anwendungsbeispiel:

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();
havan
quelle
1

Sie möchten nicht zu lange warten, bis sich das Element ändert. In diesem Code wartet der Webdriver bis zu 2 Sekunden, bevor er fortfährt.

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

user3607478
quelle
1

Da ich Seitenelementdefinitionen und Seitentestszenarien mit bereits gefundenem IWebElement für die Sichtbarkeit trenne, kann dies folgendermaßen erfolgen:

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;
        }
    };
}
Angel_D
quelle
1

Dies ist die wiederverwendbare Funktion, um mit Explicit Wait auf ein im DOM vorhandenes Element zu warten.

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;
        }
    });
}
Balakrishna
quelle
Willkommen bei Stack Overflow. Bitte senden Sie keine Nur-Code-Antworten.
JJ für Transparenz und Monica
0

Das können wir so erreichen:

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;
    }
}
Krunal
quelle
0

WebDriverWait wird nicht wirksam.

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

Dies würde sofort eine Ausnahme auslösen, sobald die Seite "interaktiv" ist. Ich weiß nicht warum, aber das Timeout wirkt so, als ob es nicht existiert.

Funktioniert vielleicht, SeleniumExtras.WaitHelpersaber ich habe es nicht versucht. Es ist offiziell, wurde aber in ein anderes Nuget-Paket aufgeteilt. Sie können sich auf C # Selenium 'ExpectedConditions is obsolet' beziehen .

Ich selbst benutze FindElementsund überprüfe, ob Count == 0, wenn wahr, verwenden await Task.Delay. Es ist wirklich nicht ganz effizient.

imba-tjd
quelle
0

Sie können Folgendes verwenden

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

Ich sehe bereits mehrere Lösungen, die großartig funktionieren! Für den Fall, dass jemand etwas anderes benötigt, dachte ich, ich würde zwei Lösungen veröffentlichen, die ich persönlich in Selen C # verwendet habe, um zu testen, ob ein Element vorhanden ist! Hoffe es hilft, Prost!

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

}

Hier ist der zweite

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

}
newITguy
quelle
-1
 new WebDriverWait(driver, TimeSpan.FromSeconds(10)).
   Until(ExpectedConditions.PresenceOfAllElementsLocatedBy((By.Id("toast-container"))));
David
quelle
ExpectedConditions ist veraltet
GELR
-1

Die erste Antwort ist gut. Mein Problem war, dass nicht behandelte Ausnahmen den Web-Treiber nicht richtig schlossen und der erste Wert, den ich verwendet hatte, 1 Sekunde betrug.

Wenn Sie das gleiche Problem bekommen

restart you visual studiound stellen Sie sicher, dass all the exceptions are handledrichtig.

Pete Kozak
quelle
Inzwischen sollten Sie wissen, dass es in Stack Overflow keine Reihenfolge für Antworten gibt, daher gibt es keine "erste Antwort"
Antti Haapala
-2

War auf der Suche, wie man in Selen auf Zustand wartet, ist in diesem Thread gelandet und hier ist, was ich jetzt benutze:

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

ReadCell(row, col) != ""kann jede Bedingung sein. So, weil:

  • es gehört mir
  • ermöglicht Inlining
Mars Robertson
quelle