web-dev-qa-db-fra.com

Selenium WebDriver Comment résoudre une exception de référence d'élément périmé?

J'ai le code suivant dans un test de pilote Web Selenium 2 qui fonctionne lorsque je suis en train de déboguer mais échoue la plupart du temps lorsque je l'exécute dans la construction. Je sais que cela doit avoir quelque chose à voir avec la façon dont la page n'est pas rafraîchie, mais je ne sais pas comment le résoudre, donc tout indicateur quant à ce que j'ai mal fait est apprécié. J'utilise JSF primefaces comme infrastructure d'application Web. Lorsque je clique sur le lien Ajouter un nouveau lien, une boîte de dialogue contextuelle apparaît avec une zone de saisie dans laquelle je peux entrer une date, puis cliquez sur Enregistrer. C'est pour obtenir un élément d'entrée dans lequel entrer du texte que je reçois une exception de référence d'élément périmé.

Merci d'avance

import static org.junit.Assert.assertEquals;

 import Java.util.HashMap;
import Java.util.List;
import Java.util.Map;

import org.junit.Test;
import org.openqa.Selenium.By;
import org.openqa.Selenium.StaleElementReferenceException;
import org.openqa.Selenium.WebDriver;
import org.openqa.Selenium.WebElement;
import org.openqa.Selenium.chrome.ChromeDriver;
import org.openqa.Selenium.support.ui.ExpectedCondition;
import org.openqa.Selenium.support.ui.WebDriverWait;


public class EnterActiveSubmissionIntegrationTest {
Map<String, Map<String, String>> tableData = new HashMap<String, Map<String, String>>();

@Test
public void testEnterActiveSubmission() throws Exception {
    // Create a new instance of the Firefox driver
    // Notice that the remainder of the code relies on the interface, 
    // not the implementation.
    System.setProperty("webdriver.chrome.driver", "C:/apps/chromedriver.exe");
    WebDriver driver = new ChromeDriver();

    // And now use this to visit Google
    driver.get("http://localhost:8080/strfingerprinting");
    // Alternatively the same thing can be done like this
    // driver.navigate().to("http://www.google.com");

    // Find the text input element by its name
    WebElement element = driver.findElement(By.linkText("Manage Submissions"));
    element.click();
    parseTableData(driver, "form:submissionDataTable_data", 1);
    assertEquals(tableData.get("form:submissionDataTable_data").get("12"), "Archived");

    WebElement newElement = driver.findElement(By.linkText("Add new"));
    newElement.click();

    WebDriverWait wait = new WebDriverWait(driver,10);
    wait.until(new ExpectedCondition<Boolean>() {
        public Boolean apply(WebDriver driver) {
            WebElement button = driver.findElement(By
                    .name("createForm:dateInput_input"));

            if (button.isDisplayed())
                return true;
            else
                return false;

        }
    });

    WebElement textElement = driver.findElement(By.name("createForm:dateInput_input"));
    textElement.sendKeys("24/04/2013");
    WebElement saveElement = driver.findElement(By.name("createForm:saveButton"));
    saveElement.click();

    driver.navigate().refresh();

    parseTableData(driver, "form:submissionDataTable_data", 2);

    //Close the browser
    driver.quit();
}



private void parseTableData(WebDriver driver, String id, int expectedRows) {
    // Check the title of the page or expected element on page
    WebElement subTableElement = driver.findElement(By.id(id));
    List<WebElement> tr_collection=subTableElement.findElements(By.xpath("id('"+ id + "')/tr"));

    assertEquals("incorrect number of rows returned", expectedRows, tr_collection.size());
    int row_num,col_num;
    row_num=1;

    if(tableData.get(id) == null) {
        tableData.put(id, new HashMap<String, String>());
    }
    Map<String, String> subTable = tableData.get(id);
    for(WebElement trElement : tr_collection)
    {
        List<WebElement> td_collection=trElement.findElements(By.xpath("td"));
        col_num=1;
        for(WebElement tdElement : td_collection)
        {
            subTable.put(row_num + "" + col_num, tdElement.getText());
            col_num++;
        }
        row_num++;
    }
}
}

Quand j’exécute ceci, j’obtiens l’exception suivante mais cela peut se produire le

WebElement textElement = driver.findElement(By.name("createForm:dateInput_input")); 

ou

if (button.isDisplayed())

trace d'exception

org.openqa.Selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document
(Session info: chrome=26.0.1410.64)
  (Driver info: chromedriver=0.8,platform=Windows NT 6.0 SP2 x86) (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 56 milliseconds
For documentation on this error, please visit:        http://seleniumhq.org/exceptions/stale_element_reference.html
Build info: version: '2.32.0', revision: '6c40c187d01409a5dc3b7f8251859150c8af0bcb', time: '2013-04-09 10:39:28'
System info: os.name: 'Windows Vista', os.Arch: 'x86', os.version: '6.0', Java.version: '1.6.0_10'
Session ID: 784c53b99ad83c44d089fd04e9a42904
Driver info: org.openqa.Selenium.chrome.ChromeDriver
Capabilities [{platform=XP, acceptSslCerts=true, javascriptEnabled=true,   browserName=chrome, rotatable=false, driverVersion=0.8, locationContextEnabled=true,  version=26.0.1410.64, cssSelectorsEnabled=true, databaseEnabled=true, handlesAlerts=true,  browserConnectionEnabled=false, nativeEvents=true, webStorageEnabled=true,   applicationCacheEnabled=false, takesScreenshot=true}]
at Sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at  Sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.Java:39)
at  Sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.Java:27)
at Java.lang.reflect.Constructor.newInstance(Constructor.Java:513)
at org.openqa.Selenium.remote.ErrorHandler.createThrowable(ErrorHandler.Java:187)
at org.openqa.Selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.Java:145)
at org.openqa.Selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.Java:554)
at org.openqa.Selenium.remote.RemoteWebElement.execute(RemoteWebElement.Java:268)
at org.openqa.Selenium.remote.RemoteWebElement.isDisplayed(RemoteWebElement.Java:320)
at com.integration.web.EnterActiveSubmissionIntegrationTest$1.apply(EnterActiveSubmissionIntegrationTest.Java:58)
at com.integration.web.EnterActiveSubmissionIntegrationTest$1.apply(EnterActiveSubmissionIntegrationTest.Java:1)
at org.openqa.Selenium.support.ui.FluentWait.until(FluentWait.Java:208)
at com.integration.web.EnterActiveSubmissionIntegrationTest.testEnterActiveSubmission(EnterActiveSubmissionIntegrationTest.Java:53)
at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
33
user1107753

Tout d’abord, clarifions ce qu’est un WebElement.

Un WebElement est une référence à un élément du DOM.

Une exception StaleElementException est levée lorsque l'élément avec lequel vous interagissiez est détruit puis recréé. La plupart des pages Web complexes de nos jours déplacent des choses à la volée au fur et à mesure que l'utilisateur interagit avec elle, ce qui nécessite la destruction et la recréation d'éléments dans le DOM.

Lorsque cela se produit, la référence à l'élément dans le DOM que vous aviez précédemment devient obsolète et vous ne pouvez plus utiliser cette référence pour interagir avec l'élément dans le DOM. Lorsque cela se produit, vous devrez actualiser votre référence ou, dans les conditions réelles, retrouver l'élément.

55
Ardesco

Ce n'est pas un problème. Si vous encapsulez votre appel .findElement dans un bloc try-catch et capturez l'exception StaleElementReferenceException, vous pouvez effectuer une boucle et une nouvelle tentative autant de fois que nécessaire jusqu'à la réussite de l'opération.

Voici quelques exemples que j'ai écrits .

Un autre exemple du projet Selenide :

public static final Condition hidden = new Condition("hidden", true) {
    @Override
    public boolean apply(WebElement element) {
      try {
        return !element.isDisplayed();
      } catch (StaleElementReferenceException elementHasDisappeared) {
        return true;
      }
    }
  };
20
djangofan

Ce qui m’arrivait à moi, c’était que webdriver trouverait une référence à un élément DOM puis javascript, à un moment ou à un autre, obtiendrait cet élément et le rajouterait (parce que la page était en train de faire un redessin).

Essaye ça. Déterminez l'action qui entraîne la suppression de l'élément dom du DOM. Dans mon cas, il s'agissait d'un appel async. Ajax, et l'élément était en train d'être supprimé du DOM lorsque l'appel ajax était terminé. Juste après cette action, attendez que l'élément soit périmé:

... do a thing, possibly async, that should remove the element from the DOM ...
wait.until(ExpectedConditions.stalenessOf(theElement));

À ce stade, vous êtes certain que l'élément est maintenant périmé. Donc, la prochaine fois que vous référencez l'élément, attendez encore, en attendant qu'il soit rajouté au DOM:

wait.until(ExpectedConditions.presenceOfElementLocated(By.id("whatever")))
17
eeeeaaii

Essayez d'attendre un élément comme celui-ci:

// Waiting 30 seconds for an element to be present on the page, checking
// for its presence once every 5 seconds.
Wait<WebDriver> stubbornWait = new FluentWait<WebDriver>(driver)
    .withTimeout(30, SECONDS)
    .pollingEvery(5, SECONDS)
    .ignoring(NoSuchElementException.class)
    .ignoring(StaleElementReferenceException.class);

WebElement foo = stubbornWait.until(new Function<WebDriver, WebElement>() {
    public WebElement apply(WebDriver driver) {
        return driver.findElement(By.id("foo"));
    }
});
9
Anurag_user3239660

Deux raisons pour l'élément Stale

  1. Un élément qui se trouve sur une page Web référencée en tant que WebElement dans WebDriver, puis les modifications du DOM (probablement en raison de fonctions JavaScript) rendant WebElement périmé.

  2. L'élément a été entièrement supprimé.

Lorsque vous essayez d'interagir avec le WebElement stabilisé [tout cas précédent], l'exception StaleElementException est levée.

Comment éviter/résoudre une exception de Stale?

  1. Stocker des localisateurs sur vos éléments au lieu de références
driver = webdriver.Firefox();
driver.get("http://www.github.com");
search_input = lambda: driver.find_element_by_name('q');
search_input().send_keys('hello world\n'); 
time.sleep(5);


search_input().send_keys('hello frank\n') // no stale element exception
  1. Tirer parti des points d'ancrage dans les bibliothèques JS utilisées
   # Using Jquery queue to get animation queue length.
    animationQueueIs = """
    return $.queue( $("#%s")[0], "fx").length;
    """ % element_id
    wait_until(lambda: self.driver.execute_script(animationQueueIs)==0)
  1. Déplacement de vos actions dans une injection JavaScript
 self.driver.execute_script("$(\"li:contains('Narendra')\").click()");
  1. Attendre de manière proactive que l'élément soit périmé
  # Wait till the element goes stale, this means the list has updated
  wait_until(lambda: is_element_stale(old_link_reference))

Cette solution, qui a fonctionné pour moi, est mentionnée ici si vous avez un scénario supplémentaire, qui a fonctionné pour vous, puis commentez ci-dessous.

8
NarendraC

StaleElementReferenceException est due à l'indisponibilité d'un élément auquel accéder par la méthode findelement.

Vous devez vous assurer avant d'effectuer toute opération sur un élément (si vous avez un doute sur la disponibilité de cet élément)

En attente de la visibilité d'un élément

(new WebDriverWait(driver, 10)).until(new ExpectedCondition()
    {
           public Boolean apply(WebDriver d) {
              return d.findElement(By.name("createForm:dateInput_input")).isDisplayed();
     }});

Sinon, utilisez la logique this pour vérifier si l'élément est présent ou non.

4
Santoshsarma

Utilisez les conditions attendues fournies par Selenium pour attendre le WebElement.

Pendant le débogage, le client n’est pas aussi rapide que si vous exécutiez simplement un test unitaire ou une construction maven. Cela signifie qu'en mode débogage, le client dispose de plus de temps pour préparer l'élément. Toutefois, si la construction utilise le même code, il est beaucoup plus rapide et le WebElement que vous recherchez peut ne pas être visible dans le DOM de la page.

Croyez-moi avec ceci, j'ai eu le même problème.

par exemple:

inClient.waitUntil(ExpectedConditions.visibilityOf(YourElement,2000))

Cette méthode simple appelle attend après son appel pendant 2 secondes sur la visibilité de votre WebElement sur DOM.

2
user2440872

J'ai résolu ce problème avec le code suivant.

public WebElement waitForElement(final By findBy, final int waitTime) {
    Wait<AppiumDriver> wait = new FluentWait<>((AppiumDriver) driver)
            .withTimeout(waitTime, TimeUnit.SECONDS)
            .pollingEvery(POLL_TIME, TimeUnit.SECONDS)
            .ignoring(NoSuchElementException.class,StaleElementReferenceException.class);

    WebElement webElement = wait.until(new Function<AppiumDriver, WebElement>() {
        @Override
        public WebElement apply(AppiumDriver driver) {
            System.out.println("Trying to find element " + findBy.toString());                
            WebElement element = driver.findElement(findBy);
            return element;
        }
    });
    return webElement;
}
1
Raghu K Nair

Quand une exception d'élément obsolète se produit !!

Une exception d'élément obsolète peut se produire lorsque les bibliothèques prenant en charge ces zones de texte/boutons/liens ont changé, ce qui signifie que les éléments sont identiques, mais que la référence a maintenant été modifiée sur le site Web sans affecter les localisateurs. Ainsi, la référence que nous avons stockée dans notre cache, y compris la référence de bibliothèque, est devenue ancienne ou obsolète car la page a été actualisée avec des bibliothèques mises à jour.

for(int j=0; j<5;j++)
try {
    WebElement elementName=driver.findElement(By.name(“createForm:dateInput_input”));
    break;
} catch(StaleElementReferenceException e){
e.toString();
System.out.println(“Stale element error, trying ::  ” + e.getMessage());
}
elementName.sendKeys(“20/06/2018”);
1
Nag Raj

Je suggère de ne pas utiliser @CachelookUp pour Selenium WebDriver pour StaleElementReferenceException.

Si vous utilisez @FindBy annotation et avoir @CacheLookUp, commentez et vérifiez.

1
Ashish Sharda

Cela a fonctionné pour moi (source ici ):

 /**
     * Attempts to click on an element multiple times (to avoid stale element
     * exceptions caused by rapid DOM refreshes)
     *
     * @param d
     *            The WebDriver
     * @param by
     *            By element locator
     */
    public static void dependableClick(WebDriver d, By by)
    {
        final int MAXIMUM_WAIT_TIME = 10;
        final int MAX_STALE_ELEMENT_RETRIES = 5;

        WebDriverWait wait = new WebDriverWait(d, MAXIMUM_WAIT_TIME);
        int retries = 0;
        while (true)
        {
            try
            {
                wait.until(ExpectedConditions.elementToBeClickable(by)).click();

                return;
            }
            catch (StaleElementReferenceException e)
            {
                if (retries < MAX_STALE_ELEMENT_RETRIES)
                {
                    retries++;
                    continue;
                }
                else
                {
                    throw e;
                }
            }
        }
    }
0
Curt

Cette solution a bien fonctionné pour moi:

Ajout de la fonction de gestion des erreurs et réessayez

var pollLoop = function () {
      element(by.id('spinnerElem')).getAttribute('class').then(function (cls) {
                if (cls.indexOf('spinner-active') > -1) {
                    // wait for the spinner
                } else {
                    //do your logic
                    promise.defer().fulfill();
                }
            }, function () {
                // This err handling function is to handle the {StaleElementReferenceError} and makes sure we find the element always.
                pollLoop();
            });
        };
0
Ashok M A

En référence à la réponse donnée par @djangofan, il semble que la solution viable consiste à conserver votre code dans le bloc try catch Dans lequel se produit une possible Staleness. Quand j'utilise ce code ci-dessous, je n'ai pas eu le problème à tout moment.

public void inputName(String name)
{
    try {
        waitForVisibilityElement(name);//My own visibility function
        findElement(By.name("customerName")).sendKeys(name);
    }
    catch (StaleElementReferenceException e)
    {
        e.getMessage();
    }
}

J'ai essayé d'utiliser la ExpectedConditions.presenceOfElementLocated(By), mais les exceptions de staleness continuent de fonctionner de manière intermittente.

J'espère que cette solution aide.

0
vkrams

Il suffit de télécharger la nouvelle extension chrome et d’utiliser le serveur Selenium 3 pour que cela fonctionne correctement).

0
virender rana

Dans mon cas, cette erreur était due au fait que je définissais l'élément ActionChains en dehors de la

def parse(self, response):

méthode lorsqu’on utilise une combinaison de sélénium et de ferraille, par exemple:

Ne fonctionne pas:

class MySpider(scrapy.Spider):
     action_chains = ActionChains(self.driver)

Déplacer action_chains = ActionChains(self.driver) à l'intérieur de la def parse(self, response): a résolu le problème, par exemple:

Travaux:

def parse(self, response):
     self.driver.get(response.url)
     action_chains = ActionChains(self.driver)
0
Ben Wilson

Utiliser webdriverwait avec ExpectedCondition dans try catch block avec pour la boucle EX: pour python

for i in range(4):
    try:
        element = WebDriverWait(driver, 120).until( \
                EC.presence_of_element_located((By.XPATH, 'xpath')))
        element.click()    
        break
    except StaleElementReferenceException:
        print "exception "
0
rajkrish06

J'ai essayé plusieurs des suggestions ci-dessus, mais la plus simple a fonctionné. Dans mon cas, c’est l’utilisation de @CachelookUp pour l’élément Web qui est à l’origine de l’exception relative aux éléments périmés. J'imagine qu'après avoir actualisé la page, la référence de l'élément n'a pas été rechargée et n'a pas réussi à trouver l'élément. Désactivation de la ligne @CachelookUp pour l'élément travaillé.

    //Search button
    @FindBy(how=How.XPATH, using =".//input[@value='Search']")  
    //@CachelookUp
    WebElement BtnSearch;
0
Silverbullet

S'il vous plaît ne confondez pas les autres entre nous, si nous ne sommes pas sûrs des réponses. C'est assez frustrant pour l'utilisateur final. La réponse simple et succincte consiste à utiliser l'annotation @CacheLookup dans webdriver. Veuillez vous référer au lien ci-dessous pour cela. Comment @CacheLookup fonctionne-t-il dans WebDriver?

0
Kishor Gaur

WebDriver doit attendre que l’élément soit localisé et que le délai d’expiration expire au bout de 10 secondes.

WebElement myDynamicElement1 = new WebDriverWait(driver, 10).until(
    ExpectedConditions.presenceOfElementLocated(
        By.name("createForm:dateInput_input")
    )
);
0
Wanping Qu

Après une analyse approfondie du problème, j'ai constaté qu'une erreur se produisait en sélectionnant les éléments DIV ajoutés pour Bootstrap uniquement. Chrome supprime ce type de DIVS et l'erreur se produit. Il est suffisant pour descendre et sélectionner un élément réel pour corriger une erreur. Par exemple, ma boîte de dialogue modale a une structure:

<div class="modal-content" uib-modal-transclude="">
    <div class="modal-header">
        ...
    </div>
    <div class="modal-body">
        <form class="form-horizontal ...">
            ...
        </form>
    <div>
<div>

La sélection de div class = "modal-body" génère une erreur, la sélection de formulaire ... fonctionne normalement.

0
user1920925

Le meilleur moyen que j’ai trouvé pour éviter les références d’éléments périmés est de ne pas utiliser PageFactory, mais de stocker les localisateurs (c.-à-d. Par éléments).

public class WebDriverFactory {

    // if you want to multithread tests, use a ThreadLocal<WebDriver> 
    // instead.
    // This also makes it so you don't have to pass around WebDriver objects
    // when instantiating new Page classes
    private static WebDriver driver = null;

    public static WebDriver getDriver() {
       return driver;
    }
    public static void setDriver(WebDriver browser)  {
       driver = browser;
    }       
}

// class to let me avoid typing out the lengthy driver.findElement(s) so 
// much
public Abstract class PageBase {
    private WebDriver driver = WebDriverFactory.getDriver();

    // using var args to let you easily chain locators
    protected By getBy(By... locator) {
      return new ByChained(locator);
    }

    protected WebElement find(By... locators) {
      return driver.findElement(getBy(locators));
    }

    protected List<WebElement> findEm(By... locators) {
      return driver.findElements(getBy(locators));
    }

    protected Select select(By... locators) {
      return new Select(getBy(locators));
    }
}

public class somePage extends PageBase {
  private static WebDriver driver = WebDriverFactory.getDriver();
  private static final By buttonBy = By.cssSelector(".btn-primary");

  public void clickButton() {
     WebDriverWait wait = new WebDriverWait(driver, 10);
     wait.until(ExpectedConditions.elementToBeClickable(buttonBy));
     find(buttonBy).click();
  }

}

J'ai une classe complète de méthodes WebDriverWait statiques que j'utilise. Et je ne me souviens pas si l'utilisation susmentionnée de WebDriver wait gérera l'exception StaleElement ou non. Sinon, vous pouvez utiliser une attente fluide, comme dans la réponse de DjangoFan. Mais le principe affiché est valable (même si cette ligne spécifique avec WebDriverWait explose).

Alors le tldr;

  1. Utilisez des localisateurs et une combinaison WebDriverWait/Fluent wait/relocalisation de l'élément vous-même. Ainsi, si votre élément devient obsolète, vous pouvez le déplacer sans avoir à dupliquer le localisateur dans un @FindBy (pour un élément initialisé pagefactory), puisqu'il n'existe pas de méthode WebElement.relocate ().
  2. Pour simplifier la vie, utilisez une classe Abstract BasePage avec des méthodes pratiques pour localiser un élément/une liste d’éléments.
0
James Affleck