134 votes

Comment éviter "StaleElementReferenceException" dans Selenium ?

J'implémente beaucoup de tests Selenium en utilisant Java. Parfois, mes tests échouent à cause d'un problème de sécurité. StaleElementReferenceException .

Pourriez-vous suggérer quelques approches pour rendre les tests plus stables ?

117voto

jspcal Points 20715

Cela peut se produire si une opération DOM se déroulant sur la page rend temporairement l'élément inaccessible. Pour tenir compte de ces cas, vous pouvez essayer d'accéder à l'élément plusieurs fois dans une boucle avant de lancer une exception.

Essayez cette excellente solution de darrelgrainger.blogspot.com :

public boolean retryingFindClick(By by) {
    boolean result = false;
    int attempts = 0;
    while(attempts < 2) {
        try {
            driver.findElement(by).click();
            result = true;
            break;
        } catch(StaleElementException e) {
        }
        attempts++;
    }
    return result;
}

79voto

Chris Points 11

J'avais ce problème par intermittence. À mon insu, BackboneJS s'exécutait sur la page et remplaçait l'élément sur lequel j'essayais de cliquer. Mon code ressemblait à ceci.

driver.findElement(By.id("checkoutLink")).click();

Ce qui est bien sûr fonctionnellement la même chose que ceci.

WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
checkoutLink.click();

Ce qui se passe parfois, c'est que le javascript remplace l'élément checkoutLink entre le moment où il est trouvé et celui où il est cliqué, par exemple.

WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
// javascript replaces checkoutLink
checkoutLink.click();

Ce qui a conduit, à juste titre, à une StaleElementReferenceException en essayant de cliquer sur le lien. Je n'ai pas trouvé de moyen fiable de dire à WebDriver d'attendre que le javascript ait fini de s'exécuter, alors voici comment j'ai fini par résoudre le problème.

new WebDriverWait(driver, timeout)
    .ignoring(StaleElementReferenceException.class)
    .until(new Predicate<WebDriver>() {
        @Override
        public boolean apply(@Nullable WebDriver driver) {
            driver.findElement(By.id("checkoutLink")).click();
            return true;
        }
    });

Ce code va continuellement essayer de cliquer sur le lien, en ignorant les StaleElementReferenceExceptions jusqu'à ce que le clic réussisse ou que le délai d'attente soit atteint. J'aime cette solution car elle vous évite d'avoir à écrire une logique de réessai et n'utilise que les constructions intégrées de WebDriver.

29voto

cocorossello Points 489

La solution de Kenny est bonne, mais elle peut être écrite de manière plus élégante.

new WebDriverWait(driver, timeout)
        .ignoring(StaleElementReferenceException.class)
        .until((WebDriver d) -> {
            d.findElement(By.id("checkoutLink")).click();
            return true;
        });

Ou aussi :

new WebDriverWait(driver, timeout).ignoring(StaleElementReferenceException.class).until(ExpectedConditions.elementToBeClickable(By.id("checkoutLink")));
driver.findElement(By.id("checkoutLink")).click();

Mais de toute façon, la meilleure solution est de s'appuyer sur la bibliothèque Selenide, qui gère ce genre de choses et plus encore. (au lieu des références d'éléments, elle gère les proxies, de sorte que vous n'avez jamais à vous occuper des éléments périmés, ce qui peut être assez difficile). Séléniure

24voto

Jim Holmes Points 1640

Généralement, cela est dû au fait que le DOM a été mis à jour et que vous essayez d'accéder à un élément mis à jour/nouveau -- mais le DOM a été rafraîchi et c'est donc une référence invalide que vous avez

Contournez ce problème en utilisant d'abord une attente explicite sur l'élément pour s'assurer que la mise à jour est terminée, puis récupérez une nouvelle référence à l'élément.

Voici un peu de code pour illustrer (adapté d'un code C# que j'utilise pour EXACTEMENT cette question) :

WebDriverWait wait = new WebDriverWait(browser, TimeSpan.FromSeconds(10));
IWebElement aRow = browser.FindElement(By.XPath(SOME XPATH HERE);
IWebElement editLink = aRow.FindElement(By.LinkText("Edit"));

//this Click causes an AJAX call
editLink.Click();

//must first wait for the call to complete
wait.Until(ExpectedConditions.ElementExists(By.XPath(SOME XPATH HERE));

//you've lost the reference to the row; you must grab it again.
aRow = browser.FindElement(By.XPath(SOME XPATH HERE);

//now proceed with asserts or other actions.

J'espère que cela vous aidera !

15voto

Christian.D Points 381

La raison pour laquelle le StaleElementReferenceException se produit a déjà été exposé : les mises à jour du DOM entre la recherche et l'action sur l'élément.

Pour le problème du clic, j'ai récemment utilisé une solution comme celle-ci :

public void clickOn(By locator, WebDriver driver, int timeout)
{
    final WebDriverWait wait = new WebDriverWait(driver, timeout);
    wait.until(ExpectedConditions.refreshed(
        ExpectedConditions.elementToBeClickable(locator)));
    driver.findElement(locator).click();
}

La partie cruciale est le "chaînage" de l'outil Selenium ExpectedConditions via le ExpectedConditions.refreshed() . Cette fonction attend et vérifie si l'élément en question a été rafraîchi pendant le délai spécifié et attend en outre que l'élément devienne cliquable.

Jetez un coup d'œil à la documentation pour la méthode rafraîchie .

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