Selenium c # Webdriver: Attendez que l'élément soit présent

185

Je veux m'assurer qu'un élément est présent avant que le webdriver ne commence à faire des choses.

J'essaie de faire fonctionner quelque chose comme ça:

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

J'ai principalement du mal à configurer la fonction anynomous.

AyKarsi
la source
3
Pour votre information, il est plus propre de construire votre durée comme celle-ci TimeSpan.FromSeconds(5). Cela rend les choses plus claires IMO
Kolob Canyon

Réponses:

159

Vous pouvez également utiliser l'attente implicite:

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

Une attente implicite consiste à indiquer à WebDriver d'interroger le DOM pendant un certain temps lors de la tentative de recherche d'un ou plusieurs éléments s'ils ne sont pas immédiatement disponibles. Le paramètre par défaut est 0. Une fois défini, l'attente implicite est définie pour la durée de vie de l'instance d'objet WebDriver.

Mike Kwan
la source
5
merci, la nouvelle syntaxe est: driver.manage (). timeouts (). implicitlyWait (10, TimeUnit.SECONDS);
Reda
20
@RedaBalkouch, la syntaxe utilisée par Mike dans sa réponse est correcte. It's C #
Diemo
3
Si vous choisissez d'utiliser des attentes implicites, veillez à ne pas utiliser des attentes explicites. Cela peut entraîner un comportement imprévisible conduisant à de mauvais résultats de test. De manière générale, je recommanderais d'utiliser des attentes explicites plutôt que des attentes implicites.
mrfreester
7
Cette méthode est désormais obsolète, vous devriez plutôt utiliser la propriété ImplicitWait:Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
Samuel Rondeau-Millaire
1
J'ai utilisé l'approche fournie et j'ai trouvé que la méthode était obsolète, comme l'a souligné Samuel. La vérification de l'existence d'un élément attend maintenant jusqu'à l'heure spécifiée.
Jim Scott
279

L'utilisation de la solution fournie par Mike Kwan peut avoir un impact sur les performances globales des tests, car l'attente implicite sera utilisée dans tous les appels FindElement. Plusieurs fois, vous voudrez que FindElement échoue immédiatement lorsqu'un élément n'est pas présent (vous testez une page mal formée, des éléments manquants, etc.). Avec l'attente implicite, ces opérations attendraient l'expiration de tout le délai avant de lever l'exception. L'attente implicite par défaut est définie sur 0 seconde.

J'ai écrit une petite méthode d'extension à IWebDriver qui ajoute un paramètre de délai (en secondes) à la FindElement()méthode. C'est assez explicite:

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

Je n'ai pas mis en cache l'objet WebDriverWait car sa création est très bon marché, cette extension peut être utilisée simultanément pour différents objets WebDriver, et je n'effectue des optimisations qu'en cas de besoin.

L'utilisation est simple:

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
la source
114
Au cas où quelqu'un se demanderait, WebDriverWaitsont de l' OpenQA.Selenium.Support.UIespace de noms et sont Selenium WebDriver Support Classeslivrés dans un package séparé appelé NuGet
Andy
5
@Ved je pourrais t'embrasser <3 Je l'ai cherché dans une autre dll: D
Adween
1
@Loudenvier Veuillez mettre la première ligne en gras pour qu'elle soit plus visible. D'autant que ce n'est pas la réponse acceptée, bien que ce soit une approche meilleure et plus précise.
Rick
5
Selenium WebDriver Support Classesest maintenant apparu sur NuGet sous le nom "Selenium.Support" , la version actuelle est 3.4.0
Eric F.
1
J'avais encore beaucoup d'erreurs jusqu'à ce que j'utilise cette ligne return wait.Until(ExpectedConditions.ElementToBeClickable(by));et cela fonctionne très bien maintenant. Attention au cas où quelqu'un d'autre obtiendrait des éléments aléatoires encore introuvables.
prospecteur
84

Vous pouvez aussi utiliser

ExpectedConditions.ElementExists

Vous allez donc rechercher une disponibilité d'élément comme celle-là

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

La source

Zain Ali
la source
1
D'accord, c'est bien plus utile qu'un simple timeout (dans les cas où vous chargez dynamiquement un objet).
keithl8041
5
Pendant que cela fonctionne. Il est maintenant marqué comme obsolète et doit donc être évité.
Adam Garner
3
Voici la nouvelle approche (non obsolète): stackoverflow.com/a/49867605/331281
Dejan
1
Notez qu'à ce moment, le DotNetSeleniumExtras.WaitHelpers(mentionné par @Dejan ci-dessus) "n'est pas maintenu, les problèmes ne seront pas résolus, les PR ne seront pas acceptés". (source: github.com/SeleniumHQ/selenium/issues/… ). Son éditeur est à la recherche d'un responsable pour le reprendre.
urig
30

Voici une variante de la solution de @ Loudenvier qui fonctionne également pour obtenir plusieurs éléments:

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
la source
7
Agréable! Je viens de l'ajouter à ma propre bibliothèque! C'est la beauté du partage de code !!!
Loudenvier
1
Je suggérerais un ajout à cela. Vous pouvez intercepter la solution NoSuchElement et renvoyer null dans cette instance. Ensuite, vous pouvez créer une méthode d'extension appelée .exists qui renvoie true à moins que IWebElement ne soit nul.
Brantley Blanchard
17

Inspirée de la solution de Loudenvier, voici une méthode d'extension qui fonctionne pour tous les objets ISearchContext, pas seulement IWebDriver, qui est une spécialisation du premier. Cette méthode prend également en charge l'attente jusqu'à ce que l'élément s'affiche.

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

Exemple d'utilisation:

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
la source
1
Si vous avez défini une attente implicite comme _webDriver.Manage().Timeouts().ImplicitlyWait(Timeout);celle-ci, elle l'emportera toujours sur la valeur de délai d'expiration que vous avez définie ici.
howcheng
Cela ne semble pas fonctionner pour moi ...? J'ai ajouté un Stopwatchautour de l'appel à la méthode d'extension et un Console.WriteLine()à l'intérieur du lambda envoyé à Until(). Le chronomètre mesurait presque exactement 60 secondes et un seul message était écrit Console. Est-ce que j'ai râté quelque chose?
urig
10

J'ai confondu la fonction anyomous avec le prédicat. Voici une petite méthode d'aide:

   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
la source
5

Vous pouvez trouver quelque chose comme ça en C #.

C'est ce que j'ai utilisé dans JUnit - Selenium

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

Importez des packages associés

Aditi
la source
1
J'ai essayé de l'utiliser aujourd'hui et VS.net me donne des avertissements: la classe OpenQA.Selenium.Support.UI.ExpectedConditions a été marquée "obsolète" et a été "migrée vers le repo DotNetSeleniumExtras" sur 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
la source
3
public bool doesWebElementExist(string linkexist)
{
     try
     {
        driver.FindElement(By.XPath(linkexist));
        return true;
     }
     catch (NoSuchElementException e)
     {
        return false;
     }
}
Madhu
la source
Le code ci-dessus sert à vérifier si un élément particulier est présent ou non.
Madhu
2

La commande clickAndWait n'est pas convertie lorsque vous choisissez le format Webdriver dans Selenium IDE. Voici la solution de contournement. Ajoutez la ligne d'attente ci-dessous. En réalité, le problème était le clic ou l'événement qui s'est produit avant celui-ci - ligne 1 dans mon code C #. Mais vraiment, assurez-vous simplement d'avoir un WaitForElement avant toute action où vous faites référence à un objet "By".

Code HTML:

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

Code C # / NUnit:

driver.FindElement(By.LinkText("z")).Click;
driver.WaitForElement(By.LinkText("xxxxx"));
driver.FindElement(By.LinkText("xxxxx")).Click();
MacGyver
la source
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'))

depuis EC, vous pouvez également choisir d'autres conditions, essayez ceci: http://selenium-python.readthedocs.org/api.html#module-selenium.webdriver.support.expected_conditions

Md. Nazmul Haque Sarker
la source
Cette question est étiquetée C #, pas Python. Cette réponse n'est pas pertinente.
Utilisateur le
2

Essayez ce code:

 New WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(Function(d) d.FindElement(By.Id("controlName")).Displayed)
Ammar Ben Hadj Amor
la source
4
Vous devez expliquer ce que vous avez fait et pourquoi cela résout le problème. Et veuillez formater votre code.
hering
1

Attente explicite

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

Exemple:

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

Utilisé Rn222 et Aknuds1 pour utiliser un ISearchContext qui renvoie soit un seul élément, soit une liste. Et un nombre minimum d'éléments peut être spécifié:

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

}

Exemple d'utilisation:

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
la source
1

Vous ne voulez pas attendre trop longtemps avant que l'élément change. Dans ce code, le pilote Web attend jusqu'à 2 secondes avant de continuer.

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

user3607478
la source
1

Étant donné que je sépare les définitions d'éléments de page et les scénarios de test de page en utilisant IWebElement déjà trouvé pour la visibilité, vous pouvez procéder comme suit:

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
la source
1

Il s'agit de la fonction réutilisable pour attendre un élément présent dans le DOM à l'aide de l'attente explicite.

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
la source
Bienvenue dans Stack Overflow, veuillez ne pas publier de réponses basées uniquement sur le code.
JJ for Transparency et Monica
0

Nous pouvons y parvenir comme ceci:

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
la source
0

WebDriverWait ne prendra pas effet.

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

Cela lèverait immédiatement une exception une fois que la page est "interactive". Je ne sais pas pourquoi mais le délai d'attente agit comme s'il n'existait pas.

Fonctionne SeleniumExtras.WaitHelperspeut- être mais je n'ai pas essayé. C'est officiel mais a été divisé en un autre paquet de pépites. Vous pouvez vous référer à C # Selenium 'ExpectedConditions is obsolete' .

Moi- même, j'utilise FindElementset vérifie si Count == 0, si c'est vrai, j'utilise await Task.Delay. Ce n'est vraiment pas tout à fait efficace.

imba-tjd
la source
0

Vous pouvez utiliser ce qui suit

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

Je vois plusieurs solutions déjà publiées qui fonctionnent très bien! Cependant, juste au cas où quelqu'un aurait besoin d'autre chose, j'ai pensé publier deux solutions que j'ai personnellement utilisées dans le sélénium C # pour tester si un élément est présent! J'espère que ça aide, bravo!

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

}

Voici le second

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

}
nouveauITguy
la source
-1
 new WebDriverWait(driver, TimeSpan.FromSeconds(10)).
   Until(ExpectedConditions.PresenceOfAllElementsLocatedBy((By.Id("toast-container"))));
David
la source
ExpectedConditions est obsolète
GELR
-1

La première réponse est bonne, mon problème était que les exceptions non gérées ne fermaient pas correctement le pilote Web et conservaient la même première valeur que j'avais utilisée, à savoir 1 seconde.

Si vous rencontrez le même problème

restart you visual studioet assurez-vous que all the exceptions are handledcorrectement.

Pete Kozak
la source
À présent, vous devez savoir qu'il n'y a pas d'ordre des réponses dans Stack Overflow, donc il n'y a pas de "première réponse"
Antti Haapala
-2

Cherchait comment attendre dans Selenium pour l'état, a atterri dans ce fil et voici ce que j'utilise maintenant:

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

ReadCell(row, col) != ""peut être n'importe quelle condition. Comme ça parce que:

  • c'est à moi
  • permet l'inlining
Mars Robertson
la source