Selenium c # Webdriver: attendere fino a quando l'elemento è presente


185

Voglio assicurarmi che un elemento sia presente prima che il webdriver inizi a fare cose.

Sto cercando di far funzionare qualcosa del genere:

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

Sto principalmente lottando su come impostare la funzione anynomous ..


3
Cordiali saluti - è più pulito costruire il tuo periodo di tempo in questo modo TimeSpan.FromSeconds(5). Rende più chiaro l'IMO
Kolob Canyon,

Risposte:


159

In alternativa puoi usare l'attesa implicita:

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

Un'attesa implicita è dire a WebDriver di eseguire il polling del DOM per un certo periodo di tempo quando si cerca di trovare uno o più elementi se non sono immediatamente disponibili. L'impostazione predefinita è 0. Una volta impostata, l'attesa implicita viene impostata per la durata dell'istanza dell'oggetto WebDriver.


5
grazie, la nuova sintassi è: driver.manage (). timeouts (). implicitlyWait (10, TimeUnit.SECONDS);
Reda,

20
@RedaBalkouch, la sintassi utilizzata da Mike nella sua risposta è corretta. È C #
Diemo il

3
Se si sceglie di utilizzare le attese implicite, fare attenzione a non utilizzare le attese esplicite. Ciò può causare comportamenti imprevedibili che portano a risultati di test errati. In generale, consiglierei di utilizzare le attese esplicite rispetto alle attese implicite.
Mrrefreester,

7
Questo metodo è ora deprecato, è necessario invece utilizzare la proprietà ImplicitWait:Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
Samuel Rondeau-Millaire

1
Ho usato l'approccio fornito e ho scoperto che il metodo era deprecato, come sottolineato da Samuel. Il controllo dell'esistenza di un articolo ora attende fino al tempo specificato.
Jim Scott,

279

L'uso della soluzione fornita da Mike Kwan potrebbe avere un impatto sulle prestazioni complessive dei test, poiché l'attesa implicita verrà utilizzata in tutte le chiamate FindElement. Molte volte vorrai che FindElement fallisca immediatamente quando un elemento non è presente (stai testando una pagina non valida, elementi mancanti, ecc.). Con l'attesa implicita queste operazioni aspetterebbero che scada l'intero timeout prima di lanciare l'eccezione. L'attesa implicita predefinita è impostata su 0 secondi.

Ho scritto un piccolo metodo di estensione su IWebDriver che aggiunge un parametro di timeout (in secondi) al FindElement()metodo. È abbastanza autoesplicativo:

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

Non ho memorizzato nella cache l'oggetto WebDriverWait in quanto la sua creazione è molto economica, questa estensione può essere utilizzata contemporaneamente per diversi oggetti WebDriver e faccio ottimizzazioni solo quando necessario.

L'uso è diretto:

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

114
Nel caso qualcuno si chieda, WebDriverWaitprovengono dallo OpenQA.Selenium.Support.UIspazio dei nomi ed è disponibile in un pacchetto separato chiamato Selenium WebDriver Support ClassesNuGet
Andy

5
@Ved potrei baciarti <3 cercandolo in una diversa dll: D
Adween

1
@Loudenvier Rendi la prima riga in grassetto in modo che sia più evidente. Soprattutto perché non è la risposta accettata, sebbene sia un approccio migliore e più preciso.
Rick,

5
Selenium WebDriver Support Classesè ora apparso su NuGet come "Selenium.Support" , la versione attuale è 3.4.0
Eric F.

1
Ho ancora avuto molti errori fino a quando non ho usato questa linea return wait.Until(ExpectedConditions.ElementToBeClickable(by));e ora funziona alla grande. Fai attenzione se qualcun altro riceve elementi casuali non ancora trovati.
cercatore

84

Puoi anche usare

ExpectedConditions.ElementExists

Quindi cercherai una disponibilità di elementi come quella

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

fonte


1
D'accordo, questo è molto più utile di un semplice timeout (nei casi in cui si sta caricando dinamicamente un oggetto).
keithl8041,

5
Mentre funziona. Ora è contrassegnato come obsoleto, quindi dovrebbe essere evitato.
Adam Garner,

3
Ecco il nuovo approccio (non deprecato): stackoverflow.com/a/49867605/331281
Dejan

1
Si noti che, al momento, il DotNetSeleniumExtras.WaitHelpers(indicato da @Dejan sopra) "non viene mantenuto, i problemi non verranno risolti, le PR non verranno accettate". (fonte: github.com/SeleniumHQ/selenium/issues/… ). Il suo editore sta cercando un manutentore che glielo sostituisca.
urig,

30

Ecco una variante della soluzione di @ Loudenvier che funziona anche per ottenere più elementi:

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
Bello! Ho appena aggiunto questo alla mia biblioteca! Questa è la bellezza del codice di condivisione !!!
Loudenvier,

1
Suggerirei un'aggiunta a quello. È possibile acquisire la soluzione NoSuchElement e restituire null in tale istanza. Quindi è possibile creare un metodo di estensione chiamato .exists che restituisce true a meno che IWebElement sia null.
Brantley Blanchard,

17

Ispirato alla soluzione di Loudenvier, ecco un metodo di estensione che funziona per tutti gli oggetti ISearchContext, non solo per IWebDriver, che è una specializzazione del primo. Questo metodo supporta anche l'attesa fino alla visualizzazione dell'elemento.

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

Esempio di utilizzo:

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
Se hai impostato un'attesa implicita come _webDriver.Manage().Timeouts().ImplicitlyWait(Timeout);quella, continuerai a superare il valore di timeout impostato qui.
howcheng,

Questo non sembra funzionare per me ...? Ho aggiunto una Stopwatchchiamata in giro al metodo di estensione e un Console.WriteLine()interno al lambda inviato Until(). Il cronometro misurava quasi esattamente 60 secondi e veniva scritto solo un messaggio Console. Mi sto perdendo qualcosa qui?
urig,

10

Ho confuso la funzione anomala con predicato. Ecco un piccolo metodo di aiuto:

   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

Puoi scoprire qualcosa del genere in C #.

Questo è quello che ho usato in JUnit - Selenium

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

Importa pacchetti correlati


1
Ho provato a usarlo oggi e VS.net mi sta dando avvertimenti: la classe OpenQA.Selenium.Support.UI.ExpectedConditions è stata contrassegnata come "deprecata" ed è stata "migrata al repository DotNetSeleniumExtras" su 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;
     }
}

Il codice sopra è per verificare se un particolare elemento è presente o meno.
Madhu,

2

Il comando clickAndWait non viene convertito quando si sceglie il formato Webdriver nell'IDE Selenium. Ecco la soluzione alternativa. Aggiungi la linea di attesa qui sotto. Realisticamente, il problema era il clic o l'evento che si è verificato prima di questa riga 1 nel mio codice C #. Ma davvero, assicurati di avere un WaitForElement prima di qualsiasi azione in cui stai facendo riferimento a un oggetto "By".

Codice HTML:

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

Codice C # / NUnit:

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

2

Pitone:

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

da EC puoi scegliere altre condizioni e prova questo: http://selenium-python.readthedocs.org/api.html#module-selenium.webdriver.support.expected_conditions


Questa domanda è taggata C #, non Python. Questa risposta è irrilevante.
Utente

2

Prova questo codice:

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

4
Dovresti spiegare cosa hai fatto e perché questo risolve il problema. E per favore formatta il tuo codice.
Hering

1

Attesa esplicita

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

Esempio:

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

1

Utilizzato Rn222 e Aknuds1 per utilizzare un ISearchContext che restituisce un singolo elemento o un elenco. E un numero minimo di elementi può essere specificato:

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

}

Esempio di utilizzo:

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

Non vuoi aspettare troppo a lungo prima che l'elemento cambi. In questo codice il webdriver attende fino a 2 secondi prima di continuare.

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


1

Dal momento che sto separando le definizioni degli elementi di pagina e gli scenari di test di pagina utilizzando IWebElement già trovato per la visibilità potrebbe essere fatto in questo modo:

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

Questa è la funzione riutilizzabile per attendere un elemento presente in DOM usando 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;
        }
    });
}

Benvenuto in Stack Overflow, per favore non pubblicare risposte di solo codice.
JJ per Transparency e Monica

0

Possiamo ottenerlo in questo modo:

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 non avrà effetto.

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

Ciò genererebbe immediatamente un'eccezione quando la pagina è "interattiva". Non so perché ma il timeout si comporta come se non esistesse.

Forse SeleniumExtras.WaitHelpersfunziona ma non ci ho provato. È ufficiale ma è stato suddiviso in un altro pacchetto nuget. Puoi fare riferimento a Selenio C # 'ExpectedConditions è obsoleto' .

Me stesso sta usando FindElementse controlla se Count == 0, se vero, usa await Task.Delay. Non è proprio abbastanza efficiente.


0

È possibile utilizzare quanto segue

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

-1

Vedo già diverse soluzioni già pubblicate che funzionano alla grande! Tuttavia, nel caso in cui qualcuno abbia bisogno di qualcos'altro, ho pensato di pubblicare due soluzioni che ho usato personalmente in selenio C # per verificare se un elemento è presente! Spero che aiuti, evviva!

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

}

Ecco il secondo

    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 è deprecato
GELR

-1

La prima risposta è buona, il mio problema era che le eccezioni non gestite non chiudevano correttamente il driver Web e mantenevano lo stesso primo valore che avevo usato che era 1 secondo.

Se riscontri lo stesso problema

restart you visual studioe assicurarsi che all the exceptions are handledcorrettamente.


Ormai dovresti sapere che non è possibile ordinare le risposte in Stack Overflow, quindi non esiste una "prima risposta"
Antti Haapala

-2

Stavo cercando come aspettare in Selenium per condizione, atterrato in questo thread ed ecco cosa uso ora:

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

ReadCell(row, col) != ""può essere qualsiasi condizione. In questo modo perché:

  • è mio
  • consente l'allineamento
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.