169 votes

WebDriver : vérifier si un élément existe ?

Comment vérifier si un élément existe avec le pilote web ?

L'utilisation d'un try catch est-elle vraiment le seul moyen possible ?

boolean present;
try {
   driver.findElement(By.id("logoutLink"));
   present = true;
} catch (NoSuchElementException e) {
   present = false;
}

2 votes

@Michael Freidgeim : avant d'accuser quelqu'un d'avoir écrit une question dublicate, vérifiez la date de création de la question pour déterminer laquelle est dublicate.

0 votes

Je ne vous en veux pas. C'est juste une façon de faire le ménage - pour relier les questions similaires entre elles. Voir Dois-je voter pour fermer une question dupliquée, même si elle est beaucoup plus récente et contient des réponses plus à jour ? "Si la nouvelle question est une meilleure question ou a de meilleures réponses, alors votez pour fermer l'ancienne question comme un doublon de la nouvelle."

295voto

Mike Kwan Points 13756

Vous pouvez aussi le faire :

driver.findElements( By.id("...") ).size() != 0

Ce qui évite le méchant try/catch

p.s.

Ou plus précisément par @JanHrcek ici

!driver.findElements(By.id("...")).isEmpty()

86 votes

Ou encore plus concis : !driver.findElements(By.id("...")).isEmpty() ;

16 votes

Ils fonctionnent bien si l'élément est présent, sinon ils prennent trop de temps.

4 votes

Et la taille n'est pas une méthode - driver.findElements( By.id("...") ).size != 0

60voto

Edd Points 2470

Je suis d'accord avec la réponse de Mike mais il y a une attente implicite de 3 secondes si aucun élément n'est trouvé qui peut être activée/désactivée, ce qui est utile si vous effectuez cette action souvent :

driver.manage().timeouts().implicitlyWait(0, TimeUnit.MILLISECONDS);
boolean exists = driver.findElements( By.id("...") ).size() != 0
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);

En mettant cela dans une méthode utilitaire, vous devriez améliorer les performances si vous effectuez beaucoup de tests.

7 votes

Comme le délai d'attente implicite peut être de 3 secondes, mais aussi une autre valeur, il faut d'abord stocker l'ancienne valeur, puis la réinitialiser ultérieurement. Mais malheureusement, vous pouvez définir la valeur, mais pas la lire -- cool api

4 votes

Il semble que le temps d'attente implicite par défaut soit de 0, ( seleniumhq.org/docs/04_webdriver_advanced.html ) Donc, à moins que vous ne l'ayez configuré pour qu'il soit plus long, cela ne devrait pas être nécessaire.

0 votes

@Ralph existe-t-il un moyen d'obtenir la valeur actuelle ?

16voto

Comme l'indique le commentaire, il s'agit de C# et non de Java, mais l'idée est la même. J'ai fait des recherches approfondies sur ce problème et, en fin de compte, FindElement renvoie toujours une exception lorsque l'élément n'existe pas. Il n'y a pas d'option surchargée qui vous permette d'obtenir null ou autre chose. Voici pourquoi je préfère cette solution aux autres.

  1. Renvoyer une liste d'éléments puis vérifier si la taille de la liste est égale à 0 fonctionne mais vous perdez ainsi en fonctionnalité. Vous ne pouvez pas faire un .click() sur une collection de liens même si la taille de la collection est de 1.
  2. Vous pourriez affirmer que l'élément existe, mais souvent cela arrête vos tests. Dans certains cas, j'ai un lien supplémentaire à cliquer selon la façon dont je suis arrivé sur cette page et je veux cliquer dessus s'il existe ou passer à autre chose.
  3. C'est seulement lent si vous ne définissez pas le délai d'attente. driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(0)) ;
  4. C'est en fait une méthode très simple et élégante une fois qu'elle est créée. En utilisant FindElementSafe au lieu de FindElement je ne "vois" pas l'affreux bloc try/catch et je peux utiliser un simple Existe méthode. Cela ressemblerait à quelque chose comme ceci :

    IWebElement myLink = driver.FindElementSafe(By.Id("myId"));
    if (myLink.Exists)
    {
       myLink.Click();
    }

Voici comment étendre IWebElement et IWebDriver.

IWebDriver.FindElementSafe

    /// <summary>
    /// Same as FindElement only returns null when not found instead of an exception.
    /// </summary>
    /// <param name="driver">current browser instance</param>
    /// <param name="by">The search string for finding element</param>
    /// <returns>Returns element or null if not found</returns>
    public static IWebElement FindElementSafe(this IWebDriver driver, By by)
    {
        try
        {
            return driver.FindElement(by);
        }
        catch (NoSuchElementException)
        {
            return null;
        }
    }

IWebElement.Existe

    /// <summary>
    /// Requires finding element by FindElementSafe(By).
    /// Returns T/F depending on if element is defined or null.
    /// </summary>
    /// <param name="element">Current element</param>
    /// <returns>Returns T/F depending on if element is defined or null.</returns>
    public static bool Exists(this IWebElement element)
    {
        if (element == null)
        { return false; }
        return true;
    }

Vous pourriez utiliser le polymorphisme pour modifier l'instance de la classe IWebDriver de FindElement, mais c'est une mauvaise idée du point de vue de la maintenance.

3 votes

Il y a deux problèmes : 1) vous vous trompez de langage : Java et non C#, 2) vous avez utilisé l'horrible solution de contournement try/catch (et c'était la question, s'il existe une autre solution que cet horrible et lent try/catch).

5 votes

Oui, c'est du C# mais le concept est le même. Try/Catch n'est laid que si vous devez le faire souvent. C'est pourquoi je l'ai encapsulé dans une méthode séparée. La solution finale est en fait très jolie. bool exists = driver.FindElementSafe(by).Exists() ;

0 votes

Try/Catch n'est pas seulement laid dans le code, mais aussi lent à l'exécution. L'encapsulation dans une méthode différente résout le premier problème, mais pas le second.

3voto

Lotzy Points 56

J'ai étendu l'implémentation de Selenium WebDriver, dans mon cas HtmlUnitDriver pour exposer une méthode

public boolean isElementPresent(By by){}

comme ça :

  1. vérifier si la page est chargée dans un délai donné.
  2. Une fois la page chargée, j'abaisse le temps d'attente implicite du WebDriver à quelques millisecondes, dans mon cas 100 mills, mais cela devrait probablement fonctionner avec 0 mills aussi.
  3. appeler findElements(By), le WebDriver, même s'il ne trouve pas l'élément, n'attendra que le temps indiqué ci-dessus.
  4. remonter le temps d'attente implicite pour le chargement futur de la page

Voici mon code :

import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

public class CustomHtmlUnitDriver extends HtmlUnitDriver {

    public static final long DEFAULT_TIMEOUT_SECONDS = 30;
    private long timeout = DEFAULT_TIMEOUT_SECONDS;

    public long getTimeout() {
        return timeout;
    }
    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }

    public boolean isElementPresent(By by) {
        boolean isPresent = true;
        waitForLoad();
        //search for elements and check if list is empty
        if (this.findElements(by).isEmpty()) {
            isPresent = false;
        }
        //rise back implicitly wait time
        this.manage().timeouts().implicitlyWait(timeout, TimeUnit.SECONDS);
        return isPresent;
    }

    public void waitForLoad() {
        ExpectedCondition<Boolean> pageLoadCondition = new ExpectedCondition<Boolean>() {
            public Boolean apply(WebDriver wd) {
                //this will tel if page is loaded
                return "complete".equals(((JavascriptExecutor) wd).executeScript("return document.readyState"));
            }
        };
        WebDriverWait wait = new WebDriverWait(this, timeout);
        //wait for page complete
        wait.until(pageLoadCondition);
        //lower implicitly wait time
        this.manage().timeouts().implicitlyWait(100, TimeUnit.MILLISECONDS);
    }   
}

Utilisation :

CustomHtmlUnitDriver wd = new CustomHtmlUnitDriver();
wd.get("http://example.org");
if (wd.isElementPresent(By.id("Accept"))) {
    wd.findElement(By.id("Accept")).click();
}
else {
    System.out.println("Accept button not found on page");
}

2voto

ran Points 269

Vous pouvez faire une assertion.

voir l'exemple

driver.asserts().assertElementFound("Page was not loaded",
By.xpath("//div[@id='actionsContainer']"),Constants.LOOKUP_TIMEOUT);

vous pouvez utiliser ceci c'est natif :

public static void waitForElementToAppear(Driver driver, By selector, long timeOutInSeconds, String timeOutMessage) {
    try {
      WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
      wait.until(ExpectedConditions.visibilityOfElementLocated(selector));
    } catch (TimeoutException e) {
      throw new IllegalStateException(timeOutMessage);
    }
  }

5 votes

Cela semble intéressant, mais pouvez-vous préciser de quel pilote il s'agit ? Le site WebDriver javadocs ne montrent pas que asserts() méthode.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X