it-swarm.com.de

Selenium WebDriver Beheben einer veralteten Elementreferenzausnahme

Ich habe den folgenden Code in einem Selenium 2-Webtreiber-Test, der beim Debuggen funktioniert, aber meistens fehlschlägt, wenn ich ihn im Build ausführe. Ich weiß, es muss etwas damit zu tun haben, wie die Seite nicht aktualisiert wird, aber ich weiß nicht, wie ich sie beheben kann. Daher sind alle Hinweise auf meine Fehler sehr willkommen. Ich verwende JSF-Primefaces als mein Webanwendungsframework. Wenn ich auf den Link Neu hinzufügen klicke, wird ein Popup-Dialogfeld mit einem Eingabefeld angezeigt, in das ich ein Datum eingeben und dann auf Speichern klicken kann. Wenn ich das Eingabeelement dazu bringe, Text einzugeben, erhalte ich eine veraltete Elementreferenz-Ausnahme.

Danke im Voraus

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

Wenn ich dies ausführe, erhalte ich die folgende Ausnahme, die jedoch am auftreten kann

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

oder

if (button.isDisplayed())

ausnahmespur

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

Lassen Sie uns zunächst klarstellen, was ein WebElement ist.

Ein WebElement ist eine Referenz auf ein Element im DOM.

Eine StaleElementException wird ausgelöst, wenn das Element, mit dem Sie interagiert haben, zerstört und anschließend neu erstellt wird. Die meisten komplexen Webseiten bewegen heutzutage Dinge im Handumdrehen, wenn der Benutzer mit ihnen interagiert, und dies erfordert, dass Elemente im DOM zerstört und neu erstellt werden.

In diesem Fall ist der Verweis auf das Element im DOM, den Sie zuvor hatten, veraltet und Sie können diesen Verweis nicht mehr für die Interaktion mit dem Element im DOM verwenden. In diesem Fall müssen Sie Ihre Referenz aktualisieren oder das Element in der realen Welt wiederfinden.

55
Ardesco

Das ist kein Problem. Wenn Sie Ihren .findElement-Aufruf in einen try-catch-Block einschließen und die StaleElementReferenceException abfangen, können Sie so oft eine Schleife ausführen und es wiederholen, bis es erfolgreich ist.

Hier sind einige Beispiele, die ich geschrieben habe .

Ein weiteres Beispiel aus Selenide Projekt:

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

Was mit mir geschah, war, dass Webdriver einen Verweis auf ein DOM-Element fand und dann irgendwann, nachdem dieser Verweis erhalten worden war, Javascript dieses Element entfernte und es erneut hinzufügte (da die Seite im Grunde genommen ein Neuzeichnen durchführte).

Versuche dies. Finden Sie heraus, durch welche Aktion das dom-Element aus dem DOM entfernt wird. In meinem Fall handelte es sich um einen asynchronen Ajax-Aufruf, und das Element wurde aus dem DOM entfernt, als der Ajax-Aufruf abgeschlossen war. Warten Sie direkt nach dieser Aktion, bis das Element veraltet ist:

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

An diesem Punkt sind Sie sicher, dass das Element jetzt veraltet ist. Wenn Sie das nächste Mal auf das Element verweisen, warten Sie erneut, bis es erneut zum DOM hinzugefügt wurde:

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

Versuchen Sie, auf ein Element wie das folgende zu warten:

// 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

Zwei Gründe für das veraltete Element

  1. Ein Element, das auf einer Webseite gefunden wird, auf die in WebDriver als WebElement verwiesen wird, und dann das DOM ändert (wahrscheinlich aufgrund von JavaScript-Funktionen), dass WebElement veraltet ist.

  2. Das Element wurde vollständig gelöscht.

Wenn Sie versuchen, mit dem blockierten WebElement zu interagieren, wird die StaleElementException ausgelöst.

Wie vermeide/löse ich veraltete Ausnahmen?

  1. Speichern von Locators in Ihren Elementen anstelle von Referenzen
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. Nutzen Sie die Hooks in den verwendeten JS-Bibliotheken
   # 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. Verschieben Sie Ihre Aktionen in JavaScript-Injection
 self.driver.execute_script("$(\"li:contains('Narendra')\").click()");
  1. Warten Sie proaktiv, bis das Element veraltet ist
  # Wait till the element goes stale, this means the list has updated
  wait_until(lambda: is_element_stale(old_link_reference))

Diese Lösung, die für mich funktioniert hat, habe ich hier erwähnt, wenn Sie ein zusätzliches Szenario haben, das für Sie funktioniert hat, dann kommentieren Sie unten

8
NarendraC

StaleElementReferenceException ist auf die Nichtverfügbarkeit eines Elements zurückzuführen, auf das die findelement-Methode zugreift.

Sie müssen sicherstellen, bevor Sie Vorgänge für ein Element ausführen. (Wenn Sie Zweifel an der Verfügbarkeit dieses Elements haben.)

Warten auf die Sichtbarkeit eines Elements

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

Oder verwenden Sie this Logik, um zu überprüfen, ob das Element vorhanden ist oder nicht.

4
Santoshsarma

Verwenden Sie die von Selenium bereitgestellten erwarteten Bedingungen, um auf das WebElement zu warten.

Während Sie debuggen, ist der Client nicht so schnell, als würden Sie nur einen Unit-Test oder einen Maven-Build ausführen. Dies bedeutet, dass der Client im Debug-Modus mehr Zeit hat, um das Element vorzubereiten. Wenn der Build jedoch denselben Code ausführt, ist er viel schneller und das von Ihnen gesuchte WebElement ist möglicherweise nicht im DOM der Seite sichtbar.

Vertrauen Sie mir damit, ich hatte das gleiche Problem.

beispielsweise:

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

Diese einfachen Methodenaufrufe warten nach seinem Aufruf 2 Sekunden auf die Sichtbarkeit Ihres WebElement in DOM.

2
user2440872

Ich habe dieses Problem mit dem folgenden Code gelöst.

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

Wenn eine veraltete Elementausnahme auftritt !!

Eine veraltete Elementausnahme kann auftreten, wenn sich die Bibliotheken, die diese Textfelder/Schaltflächen/Links unterstützen, geändert haben. Dies bedeutet, dass die Elemente identisch sind, die Referenz auf der Website jedoch geändert wurde, ohne dass sich dies auf die Locators auswirkt. Daher ist die Referenz, die wir in unserem Cache gespeichert haben, einschließlich der Bibliotheksreferenz, jetzt alt oder veraltet, da die Seite mit aktualisierten Bibliotheken aktualisiert wurde.

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

Ich würde vorschlagen, @CachelookUp Für Selenium WebDriver für StaleElementReferenceException nicht zu verwenden.

Wenn Sie die Annotation @FindBy Verwenden und @CacheLookUp Haben, kommentieren Sie sie einfach aus und überprüfen Sie sie.

1
Ashish Sharda

Das hat bei mir funktioniert (Quelle hier ):

 /**
     * 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

Diese Lösung hat bei mir gut funktioniert:

Fehlerbehandlungsfunktion hinzufügen und erneut versuchen

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

In Bezug auf die Antwort von @djangofan sieht es so aus, als ob die praktikable Lösung darin besteht, Ihren Code im try catch - Block zu belassen, in dem eine mögliche Veraltenheit auftritt. Wenn ich diesen Code verwende, habe ich das Problem zu keinem Zeitpunkt erhalten.

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

Ich habe versucht, die Funktion ExpectedConditions.presenceOfElementLocated(By) zu verwenden, aber die Ausnahmen für die Veralten werfen immer noch zeitweise Ausnahmen auf.

Hoffe, diese Lösung hilft.

0
vkrams

Laden Sie einfach die neue chrome Erweiterung herunter und verwenden Sie Selenium Server 3, es wird gut funktionieren.

0
virender rana

In meinem Fall wurde dieser Fehler durch die Tatsache verursacht, dass ich das ActionChains-Element außerhalb von definierte

def parse(self, response):

methode bei Verwendung einer Kombination von Selen und Scrapy, z.B.

Funktioniert nicht:

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

Das Verschieben von action_chains = ActionChains(self.driver) innerhalb von def parse(self, response): löste das Problem, z.

Werke:

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

Verwenden Sie webdriverwait mit ExpectedCondition im try catch-Block mit for loop EX: for 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

Ich habe viele der oben genannten Vorschläge ausprobiert, aber der einfachste hat funktioniert. In meinem Fall verursachte die Verwendung von @CachelookUp für das Webelement die Ausnahme für veraltete Elemente. Ich denke, nach dem Aktualisieren der Seite wurde die Elementreferenz nicht neu geladen und konnte das Element nicht finden. Das Deaktivieren der @ CachelookUp-Zeile für das Element hat funktioniert.

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

Bitte verwirren Sie andere nicht untereinander, wenn wir uns bei den Antworten nicht sicher sind. Es ist ziemlich frustrierend für den Endbenutzer. Die einfache und kurze Antwort ist die Verwendung der @ CacheLookup-Annotation in Webdriver. Bitte beziehen Sie sich auf den folgenden Link. Wie funktioniert @CacheLookup in WebDriver?

0
Kishor Gaur

Der WebDriver muss warten, bis das Element gefunden wurde und nach 10 Sekunden eine Zeitüberschreitung eintritt.

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

Nach eingehender Untersuchung des Problems stellte ich fest, dass ein Fehler bei der Auswahl von DIV-Elementen auftritt, die nur für Bootstrap= hinzugefügt wurden. Chrome= Browser entfernt solche DIVS und der Fehler tritt auf reicht aus, um zurückzutreten und ein echtes Element zur Behebung eines Fehlers auszuwählen. Mein modaler Dialog hat beispielsweise folgende Struktur:

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

Die Auswahl von div class = "modal-body" erzeugt einen Fehler. Die Auswahl von form ... funktioniert wie gewohnt.

0
user1920925

Die beste Möglichkeit, veraltete Elementreferenzen zu vermeiden, besteht darin, nicht die PageFactory zu verwenden, sondern die Locators (d. H. By-Elemente) zu speichern.

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

}

Ich habe eine Klasse voller statischer WebDriverWait-Methoden, die ich verwende. Und ich erinnere mich nicht, ob die obige Verwendung von WebDriver wait die StaleElement-Ausnahme behandelt oder nicht. Wenn nicht, könnten Sie ein flüssiges Warten verwenden, wie in der Antwort von DjangoFan. Aber das Prinzip, das ich anzeige, wird funktionieren (auch wenn diese bestimmte Zeile mit dem WebDriverWait explodiert.

Also der tldr;

  1. Verwenden Sie Locators und eine Kombination aus WebDriverWait/Fluent wait/das Element selbst verschieben. Wenn Ihr Element veraltet ist, können Sie es verschieben, ohne den Locator in einem @FindBy Duplizieren zu müssen (für ein pagefactory-initialisiertes Element). da es keine WebElement.relocate () Methode gibt.
  2. Verwenden Sie zur Vereinfachung des Lebens eine Abstract BasePage-Klasse mit praktischen Methoden zum Auffinden eines Elements/einer Liste von Elementen.
0
James Affleck