150 votes

Exception StaleElementReferenceException aléatoire "L'élément n'est plus attaché au DOM".

J'espère que ce n'est que moi, mais Selenium Webdriver semble être un véritable cauchemar. Le webdriver Chrome est actuellement inutilisable, et les autres pilotes sont assez peu fiables, du moins c'est ce qu'il semble. Je me bats contre de nombreux problèmes, mais en voici un.

De manière aléatoire, mes tests échouent avec un

"org.openqa.selenium.StaleElementReferenceException: Element is no longer attached 
to the DOM    
System info: os.name: 'Windows 7', os.arch: 'amd64',
 os.version: '6.1', java.version: '1.6.0_23'"

J'utilise la version 2.0b3 de webdriver. J'ai vu ce phénomène se produire avec les pilotes FF et IE. La seule façon d'éviter cela est d'ajouter un appel réel à Thread.sleep avant que l'exception ne se produise. J'espère donc que quelqu'un pourra me signaler une erreur de ma part qui permettra d'améliorer la situation.

27 votes

J'espère que les 17 000 vues indiquent que ce n'est pas seulement vous ;) Cela doit être l'exception Selenium la plus frustrante qui soit.

4 votes

48k maintenant ! J'ai le même problème...

3 votes

Je trouve que le Sélénium est une pure et complète poubelle. ....

122voto

jarib Points 4494

Oui, si vous avez des problèmes avec les StaleElementReferenceExceptions, c'est parce qu'il y a une condition de course. Considérez le scénario suivant :

WebElement element = driver.findElement(By.id("foo"));
// DOM changes - page is refreshed, or element is removed and re-added
element.click();

Maintenant, au moment où vous cliquez sur l'élément, la référence de l'élément n'est plus valide. Il est pratiquement impossible pour WebDriver de deviner tous les cas où cela pourrait se produire - il se défausse donc et vous laisse le contrôle, vous qui, en tant qu'auteur du test ou de l'application, devriez savoir exactement ce qui peut ou ne peut pas se produire. Ce que vous voulez faire, c'est attendre explicitement que le DOM soit dans un état où vous savez que les choses ne changeront pas. Par exemple, en utilisant un WebDriverWait pour attendre qu'un élément spécifique existe :

// times out after 5 seconds
WebDriverWait wait = new WebDriverWait(driver, 5);

// while the following loop runs, the DOM changes - 
// page is refreshed, or element is removed and re-added
wait.until(presenceOfElementLocated(By.id("container-element")));        

// now we're good - let's click the element
driver.findElement(By.id("foo")).click();

La méthode presenceOfElementLocated() ressemblerait à ceci :

private static Function<WebDriver,WebElement> presenceOfElementLocated(final By locator) {
    return new Function<WebDriver, WebElement>() {
        @Override
        public WebElement apply(WebDriver driver) {
            return driver.findElement(locator);
        }
    };
}

Vous avez tout à fait raison de dire que le pilote Chrome actuel est assez instable, et vous serez heureux d'apprendre que le tronc Selenium comporte un pilote Chrome réécrit, où la plupart de l'implémentation a été faite par les développeurs de Chromium dans le cadre de leur arbre.

PS. Au lieu d'attendre explicitement comme dans l'exemple ci-dessus, vous pouvez activer les attentes implicites - de cette façon, WebDriver bouclera toujours jusqu'au timeout spécifié en attendant que l'élément devienne présent :

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS)

Mais d'après mon expérience, l'attente explicite est toujours plus fiable.

2 votes

Ai-je raison de dire qu'il n'est plus possible de lire des éléments dans des variables et de les réutiliser ? Parce que j'ai un énorme DSL WATiR sec et dynamique qui repose sur le passage d'éléments et j'essaie de le porter sur webdriver, mais je rencontre le même problème. En fait, je dois ajouter du code pour relire tous les éléments du module à chaque étape de test qui modifie le DOM...

0 votes

Salut. Puis-je vous demander quel est le type de Fonction dans cet exemple ? Je n'arrive pas à le trouver..... MERCI !

1 votes

@Hannibal : com.google.common.base.Function<F, T> fourni par Goyave .

10voto

aearon Points 192

J'ai pu utiliser une méthode comme celle-ci avec un certain succès :

WebElement getStaleElemById(String id) {
    try {
        return driver.findElement(By.id(id));
    } catch (StaleElementReferenceException e) {
        System.out.println("Attempting to recover from StaleElementReferenceException ...");
        return getStaleElemById(id);
    }
}

Oui, il continue de sonder l'élément jusqu'à ce qu'il ne soit plus considéré comme périmé (frais ?). Cela ne va pas vraiment à la racine du problème, mais j'ai constaté que le WebDriver peut être assez pointilleux sur le lancement de cette exception - parfois je l'obtiens, et parfois non. Ou il se peut que le DOM soit vraiment en train de changer.

Je ne suis donc pas tout à fait d'accord avec la réponse ci-dessus selon laquelle cela indique nécessairement un test mal écrit. Je l'ai obtenu sur des pages récentes avec lesquelles je n'ai pas interagi de quelque manière que ce soit. Je pense qu'il y a un certain flottement soit dans la façon dont le DOM est représenté, soit dans ce que WebDriver considère comme stale.

7 votes

Vous avez un bug dans ce code, vous ne devriez pas continuer à appeler récursivement la méthode sans une sorte de cap ou vous allez faire sauter votre pile.

3 votes

Je pense qu'il est préférable d'ajouter un compteur ou quelque chose, de sorte que lorsque nous obtenons l'erreur à plusieurs reprises, nous pouvons effectivement lancer l'erreur. Sinon, s'il y a vraiment une erreur, vous vous retrouverez dans une boucle.

0 votes

Je suis d'accord pour dire que ce n'est pas le résultat de tests mal écrits. Selenium a tendance à faire cela sur les sites web modernes, même pour les tests les mieux écrits - probablement parce que les sites web rafraîchissent continuellement leurs éléments via les liaisons bidirectionnelles qui sont courantes dans les frameworks d'applications web réactives, même si aucune modification de ces éléments n'est nécessaire. Une méthode comme celle-ci devrait faire partie de chaque framework Selenium qui teste une application web moderne.

10voto

Eero Points 1612

Je reçois parfois cette erreur lorsque les mises à jour AJAX sont à mi-chemin. Capybara semble être assez intelligent pour attendre les modifications du DOM (cf. Pourquoi wait_until a été supprimé de Capybara ), mais le temps d'attente par défaut de 2 secondes n'était tout simplement pas suffisant dans mon cas. Changé dans _spec_helper.rb_ avec par ex.

Capybara.default_max_wait_time = 5

2 votes

Cela a également résolu mon problème : j'obtenais un StaleElementReferenceError et l'augmentation de Capybara.default_max_wait_time a résolu le problème.

1voto

Iwan1993 Points 62

J'ai été confronté au même problème aujourd'hui et j'ai créé une classe wrapper, qui vérifie avant chaque méthode si la référence de l'élément est toujours valide. Ma solution pour récupérer l'élément est assez simple, alors j'ai pensé que je devais la partager.

private void setElementLocator()
{
    this.locatorVariable = "selenium_" + DateTimeMethods.GetTime().ToString();
    ((IJavaScriptExecutor)this.driver).ExecuteScript(locatorVariable + " = arguments[0];", this.element);
}

private void RetrieveElement()
{
    this.element = (IWebElement)((IJavaScriptExecutor)this.driver).ExecuteScript("return " + locatorVariable);
}

Vous voyez, je "localise" ou plutôt je sauvegarde l'élément dans une variable js globale et je récupère l'élément si nécessaire. Si la page est rechargée, cette référence ne fonctionnera plus. Mais tant que seules des modifications sont apportées à Doom, la référence reste. Et cela devrait faire l'affaire dans la plupart des cas.

Cela évite également de rechercher à nouveau l'élément.

John

-1voto

En Java 8, vous pouvez utiliser très méthode simple pour ça :

private Object retryUntilAttached(Supplier<Object> callable) {
    try {
        return callable.get();
    } catch (StaleElementReferenceException e) {
        log.warn("\tTrying once again");
        return retryUntilAttached(callable);
    }
}

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