Selenium c # Webdriver: Espere hasta que el elemento esté presente

185

Quiero asegurarme de que haya un elemento presente antes de que el controlador web comience a hacer cosas.

Estoy tratando de hacer que algo como esto funcione:

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

Principalmente estoy luchando por cómo configurar la función anynomous ...

AyKarsi
fuente
3
FYI: es más limpio construir tu intervalo de tiempo de esta manera TimeSpan.FromSeconds(5). Lo deja más claro IMO
Kolob Canyon

Respuestas:

159

Alternativamente, puede usar la espera implícita:

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

Una espera implícita es decirle a WebDriver que sondee el DOM durante un cierto período de tiempo cuando intente encontrar un elemento o elementos si no están disponibles de inmediato. La configuración predeterminada es 0. Una vez establecida, la espera implícita se establece durante la vida de la instancia del objeto WebDriver.

Mike Kwan
fuente
55
gracias, la nueva sintaxis es: driver.manage (). timeouts (). implicitlyWait (10, TimeUnit.SECONDS);
Reda
20
@RedaBalkouch, la sintaxis que Mike usó en su respuesta es correcta. Es C #
Diemo
3
Si elige usar esperas implícitas, tenga cuidado de no usar esperas explícitas. Eso puede causar un comportamiento impredecible que conduce a malos resultados de la prueba. En términos generales, recomendaría usar esperas explícitas sobre esperas implícitas.
mrfreester
77
Este método ahora está en desuso, en su lugar debería usar la propiedad ImplicitWait:Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
Samuel Rondeau-Millaire
1
Utilicé el enfoque proporcionado y descubrí que el método estaba en desuso, como señaló Samuel. Comprobar la existencia de un elemento ahora espera hasta el tiempo especificado.
Jim Scott
279

El uso de la solución proporcionada por Mike Kwan puede tener un impacto en el rendimiento general de las pruebas, ya que la espera implícita se utilizará en todas las llamadas FindElement. Muchas veces querrá que FindElement falle inmediatamente cuando un elemento no esté presente (está probando una página con formato incorrecto, elementos faltantes, etc.). Con la espera implícita, estas operaciones esperarían a que expire todo el tiempo de espera antes de lanzar la excepción. La espera implícita predeterminada se establece en 0 segundos.

He escrito un pequeño método de extensión en IWebDriver que agrega un parámetro de tiempo de espera (en segundos) al FindElement()método. Es bastante claro:

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

No almacené en caché el objeto WebDriverWait ya que su creación es muy barata, esta extensión se puede usar simultáneamente para diferentes objetos WebDriver, y solo hago optimizaciones cuando finalmente se necesita.

El uso es sencillo:

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
fuente
114
En caso de que alguien se pregunte, WebDriverWaites del OpenQA.Selenium.Support.UIespacio de nombres y viene en un paquete separado llamado Selenium WebDriver Support ClassesNuGet
Andy
55
@Ved podría besarte <3 lo estuve buscando en un dll diferente: D
Entre el
1
@Loudenvier Por favor, ponga la primera línea en negrita para que sea más notable. Sobre todo porque no es la respuesta aceptada, aunque es un enfoque mejor y más preciso.
Rick
55
Selenium WebDriver Support Classesahora aparece en NuGet como "Selenium.Support" , la versión actual es 3.4.0
Eric F.
1
Todavía tenía muchos errores hasta que usé esta línea return wait.Until(ExpectedConditions.ElementToBeClickable(by));y ahora funciona muy bien. Aviso en caso de que alguien más obtenga elementos aleatorios que aún no se encuentran.
prospector
84

También puedes usar

ExpectedConditions.ElementExists

Entonces buscará una disponibilidad de elementos como esa

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

Fuente

Zain Ali
fuente
1
De acuerdo, esto es mucho más útil que un simple tiempo de espera (en los casos en que está cargando dinámicamente un objeto).
keithl8041
55
Mientras esto funciona. Ahora está marcado como obsoleto, por lo que debe evitarse.
Adam Garner
3
Aquí está el nuevo enfoque (no obsoleto): stackoverflow.com/a/49867605/331281
Dejan
1
Tenga en cuenta que, en este momento, el DotNetSeleniumExtras.WaitHelpers(mencionado por @Dejan arriba) "no se mantiene, los problemas no se solucionarán, los RP no serán aceptados". (fuente: github.com/SeleniumHQ/selenium/issues/… ). Su editor está buscando un mantenedor para que se lo quite.
urig
30

Aquí hay una variación de la solución de @ Loudenvier que también funciona para obtener múltiples elementos:

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
fuente
77
¡Agradable! ¡Acabo de agregar esto a mi propia biblioteca! ¡Esa es la belleza de compartir código!
Loudenvier el
1
Sugeriría una adición a eso. Podría tomar la solución NoSuchElement y devolver nulo en esa instancia. Luego, podría crear un método de extensión llamado .exists que devuelva verdadero a menos que IWebElement sea nulo.
Brantley Blanchard
17

Inspirado en la solución de Loudenvier, aquí hay un método de extensión que funciona para todos los objetos ISearchContext, no solo IWebDriver, que es una especialización de los primeros. Este método también admite esperar hasta que se muestre el 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;
        });
    }
}

Ejemplo de uso:

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
fuente
1
Si ha establecido una espera implícita como _webDriver.Manage().Timeouts().ImplicitlyWait(Timeout);esa, aún prevalecerá el valor de tiempo de espera que establezca aquí.
howcheng
Esto no parece funcionar para mí ...? Agregué un Stopwatchalrededor de la llamada al método de extensión y un Console.WriteLine()dentro de la lambda enviada a Until(). El cronómetro midió casi exactamente 60 segundos y solo se escribió un mensaje Console. ¿Me estoy perdiendo de algo?
urig
10

Confundí la función anyomous con predicado. Aquí hay un pequeño método de ayuda:

   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
fuente
5

Puedes encontrar algo como esto en C #.

Esto es lo que usé en JUnit - Selenium

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

Importar paquetes relacionados

Aditi
fuente
1
Intenté usar esto hoy y VS.net me está dando advertencias: la clase OpenQA.Selenium.Support.UI.ExpectedConditions ha sido marcada como "obsoleta" y fue "migrada al repositorio DotNetSeleniumExtras" en 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
fuente
3
public bool doesWebElementExist(string linkexist)
{
     try
     {
        driver.FindElement(By.XPath(linkexist));
        return true;
     }
     catch (NoSuchElementException e)
     {
        return false;
     }
}
Madhu
fuente
El código anterior es para verificar si un elemento en particular está presente o no.
Madhu
2

El comando clickAndWait no se convierte cuando elige el formato Webdriver en Selenium IDE. Aquí está la solución. Agregue la línea de espera a continuación. Siendo realistas, el problema fue el clic o evento que ocurrió antes de esta línea 1 en mi código C #. Pero realmente, solo asegúrese de tener un WaitForElement antes de cualquier acción en la que haga referencia a un objeto "Por".

Código HTML:

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

C # / N Código de unidad:

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

Pitón:

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

desde EC puede elegir otras condiciones, así que pruebe esto: http://selenium-python.readthedocs.org/api.html#module-selenium.webdriver.support.expected_conditions

Md. Nazmul Haque Sarker
fuente
Esta pregunta está etiquetada con C #, no con Python. Esta respuesta es irrelevante.
Usuario
2

Prueba este código:

 New WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(Function(d) d.FindElement(By.Id("controlName")).Displayed)
Ammar Ben Hadj Amor
fuente
44
Debe explicar lo que ha hecho y por qué esto resuelve el problema. Y por favor formatee su código.
areing
1

Espera explícita

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

Ejemplo:

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

Usé Rn222 y Aknuds1 para usar un ISearchContext que devuelve un solo elemento o una lista. Y se puede especificar un número mínimo de elementos:

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

}

Ejemplo de uso:

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
fuente
1

No desea esperar demasiado antes de que cambie el elemento. En este código, el controlador web espera hasta 2 segundos antes de continuar.

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

usuario3607478
fuente
1

Como estoy separando las definiciones de elementos de página y los escenarios de prueba de página usando IWebElement ya encontrado para la visibilidad, podría hacerse así:

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
fuente
1

Esta es la función reutilizable para esperar un elemento presente en DOM usando Espera explícita.

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
fuente
Bienvenido a Stack Overflow, no publique respuestas de solo código.
JJ por Transparencia y Mónica
0

Podemos lograr eso así:

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
fuente
0

WebDriverWait No tendrá efecto.

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

Esto arrojaría inmediatamente una excepción una vez que la página sea "interactiva". No sé por qué, pero el tiempo de espera actúa como si no existiera.

Quizás SeleniumExtras.WaitHelpersfunciona pero no lo intenté. Es oficial pero se dividió en otro paquete nuget. Puede consultar C # Selenium 'ExpectedConditions is obsoleto' .

Yo mismo estoy usando FindElementsy verifico si Count == 0, si es cierto, lo uso await Task.Delay. Realmente no es del todo eficiente.

imba-tjd
fuente
0

Puedes usar lo siguiente

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

¡Veo varias soluciones ya publicadas que funcionan muy bien! Sin embargo, en caso de que alguien necesite algo más, ¡pensé que publicaría dos soluciones que utilicé personalmente en selenio C # para probar si un elemento está presente! Espero que ayude, saludos!

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

}

Aqui esta el segundo

    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
fuente
-1
 new WebDriverWait(driver, TimeSpan.FromSeconds(10)).
   Until(ExpectedConditions.PresenceOfAllElementsLocatedBy((By.Id("toast-container"))));
david
fuente
Condición
-1

La primera respuesta es buena, mi problema fue que las excepciones no controladas no cerraron correctamente el controlador web y mantuvieron el mismo primer valor que había usado, que era 1 segundo.

Si tienes el mismo problema

restart you visual studioy asegúrate de eso all the exceptions are handledcorrectamente.

Pete Kozak
fuente
A estas alturas ya debe saber que no hay ordenación de respuestas en Stack Overflow, por lo que no hay una "primera respuesta"
Antti Haapala
-2

Estaba buscando cómo esperar en Selenium por la condición, aterricé en este hilo y esto es lo que uso ahora:

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

ReadCell(row, col) != ""Puede ser cualquier condición. Me gusta de esta manera porque:

  • es mio
  • permite en línea
Mars Robertson
fuente