wake-up-neo.com

Wie kann eine Ausnahme für das veraltete Element behoben werden? wenn das Element nicht mehr an das DOM angehängt ist?

Ich habe eine Frage zu "Element ist nicht mehr an das DOM gebunden".

Ich habe verschiedene Lösungen ausprobiert, aber sie arbeiten zeitweise. Bitte schlagen Sie eine Lösung vor, die dauerhaft sein könnte.

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

WebElement getStaleElemByCss(String css, WebDriver driver) {
    try {
        return driver.findElement(By.cssSelector(css));
    } catch (StaleElementReferenceException e) {
        System.out.println("Attempting to recover from StaleElementReferenceException ...");
        return getStaleElemByCss(css, driver);
    } catch (NoSuchElementException ele) {
         System.out.println("Attempting to recover from NoSuchElementException ...");
         return getStaleElemByCss(css, driver);
    }
}

Danke, Anu

14
user2498024

Das Problem

Das Problem, dem Sie wahrscheinlich gegenüberstehen, besteht darin, dass die Methode das richtige (und gültige!) Element zurückgibt, aber wenn Sie versuchen, eine Sekunde später darauf zuzugreifen, ist sie veraltet und wirft.

Dies tritt normalerweise auf, wenn:

  1. Sie klicken auf etwas, das eine neue Seite asynchron lädt oder zumindest ändert.
  2. Sie suchen sofort (bevor das Laden der Seite beendet werden konnte) nach einem Element ... und Sie finden es!
  3. Die Seite wird schließlich entladen und die neue Seite wird geladen.
  4. Sie versuchen, auf Ihr zuvor gefundenes Element zuzugreifen, aber jetzt ist es veraltet, obwohl die neue Seite es ebenfalls enthält.

Die Lösungen

Es gibt vier Lösungsmöglichkeiten, die ich kenne:

  1. Verwenden Sie richtige Wartezeiten

    Verwenden Sie nach jedem erwarteten Laden der Seite korrekte Wartezeiten, wenn Sie asynchronen Seiten gegenüberstehen. Fügen Sie nach dem ersten Klick eine explizite Wartezeit ein und warten Sie, bis die neue Seite/der neue Inhalt geladen wird. Erst danach können Sie versuchen, nach dem gewünschten Element zu suchen. Dies sollte das erste sein, was Sie tun werden. Dies erhöht die Robustheit Ihrer Tests erheblich.

  2. So wie du es getan hast

    Ich verwende seit zwei Jahren eine Variante Ihrer Methode (zusammen mit der oben genannten Technik in Lösung 1), und sie funktioniert in den meisten Fällen meistens und versagt nur bei merkwürdigen WebDriver-Fehlern. Versuchen Sie, auf das gefundene Element zuzugreifen, nachdem es gefunden wurde (bevor Sie von der Methode zurückkehren), über eine .isDisplayed()-Methode oder etwas anderes. Wenn es wirft, können Sie bereits wieder suchen. Wenn es besteht, haben Sie eine weitere (falsche) Sicherheit.

  3. Verwenden Sie ein WebElement, das sich im veralteten Zustand wiederfindet

    Schreibe einen WebElement-Dekorateur, der sich daran erinnert, wie er gefunden wurde, und ihn wiederfinden, wenn auf ihn zugegriffen wird und wirft. Dies zwingt Sie offensichtlich dazu, benutzerdefinierte findElement()-Methoden zu verwenden, die Instanzen Ihres Dekorators zurückgeben würden (oder, noch besser, eine dekorierte WebDriver, die Ihre Instanzen von den üblichen findElement()- und findElemens()-Methoden zurückgibt). Mach es so:

    public class NeverStaleWebElement implements WebElement {
        private WebElement element;
        private final WebDriver driver;
        private final By foundBy;
    
        public NeverStaleWebElement(WebElement element, WebDriver driver, By foundBy) {
            this.element = element;
            this.driver = driver;
            this.foundBy = foundBy;
        }
    
        @Override
        public void click() {
            try {
                element.click();
            } catch (StaleElementReferenceException e) {
                // log exception
    
                // assumes implicit wait, use custom findElement() methods for custom behaviour
                element = driver.findElement(foundBy);
    
                // recursion, consider a conditioned loop instead
                click();
            }
        }
    
        // ... similar for other methods, too
    
    }
    

    Beachten Sie, dass die foundBy-Informationen zwar aus den generischen WebElements zugänglich sein sollten, um dies zu vereinfachen, aber Selenium-Entwickler halten es für einen Fehler, so etwas zu versuchen, und haben nicht diese Informationen öffentlich gemacht . Es ist wohl eine schlechte Praxis, veraltete Elemente erneut zu finden, da Sie Elemente implizit neu finden, ohne dass ein Mechanismus gefunden wird, um zu prüfen, ob dies gerechtfertigt ist. Der Wiederauffindungsmechanismus könnte möglicherweise ein völlig anderes Element und nicht das gleiche wiederfinden. Auch mit findElements() schlägt es schrecklich fehl, wenn es viele gefundene Elemente gibt (entweder müssen Sie das erneute Finden von Elementen, die von findElements() gefunden wurden, nicht zulassen, oder sich daran erinnern, wie viele Elemente Ihr Element aus der zurückgegebenen List stammt).

    Ich denke, es wäre manchmal nützlich, aber es stimmt, dass niemand die Optionen 1 und 2 verwenden würde, die offensichtlich viel bessere Lösungen für die Robustheit Ihrer Tests sind. Verwenden Sie sie und erst, wenn Sie sicher sind, dass Sie das brauchen, machen Sie es.

  4. Verwenden Sie eine Aufgabenwarteschlange (die vergangene Aufgaben erneut ausführen kann)

    Implementieren Sie Ihren gesamten Workflow auf neue Weise!

    • Erstellen Sie eine zentrale Warteschlange für die Ausführung von Jobs. Diese Warteschlange soll an vergangene Jobs erinnern.
    • Implementieren Sie jede benötigte Aufgabe ("ein Element finden und darauf klicken", "ein Element finden und Schlüssel senden" usw.) über die Befehlsmustermethode. Fügen Sie nach dem Aufruf die Aufgabe der zentralen Warteschlange hinzu, die dann (entweder synchron oder asynchron, egal) ausgeführt wird.
    • Kommentieren Sie jede Aufgabe nach Bedarf mit @LoadsNewPage, @Reversible usw.
    • Die meisten Ihrer Aufgaben werden ihre Ausnahmen selbst erledigen, sie sollten eigenständig sein.
    • Wenn die Warteschlange auf eine Ausnahme für veraltete Elemente stößt, wird die letzte Aufgabe aus dem Aufgabenverlauf genommen und erneut ausgeführt, um es erneut zu versuchen.

    Dies würde natürlich viel Mühe kosten und wenn er nicht sehr gut durchdacht ist, könnte er bald nach hinten losgehen. Ich verwendete eine (wesentlich komplexere und leistungsfähigere) Variante, um fehlgeschlagene Tests fortzusetzen, nachdem ich die Seite, auf der sie sich befanden, manuell korrigiert hatte. Unter bestimmten Bedingungen (z. B. bei einer StaleElementException) würde ein Fehler den Test nicht sofort beenden, sondern warten (vor dem endgültigen Timeout nach 15 Sekunden), ein Informationsfenster öffnen und dem Benutzer die Option zur manuellen Aktualisierung geben die Seite/klicken Sie auf die rechte Schaltfläche/fixieren Sie das Formular/was auch immer. Es würde dann die fehlgeschlagene Task erneut ausführen oder sogar die Möglichkeit geben, einige Schritte in der Historie zurückzusetzen (z. B. bis zum letzten @LoadsNewPage-Job).


Letzte Nitpicks

Alles in allem könnte Ihre ursprüngliche Lösung etwas polieren. Sie können die beiden Methoden zu einer allgemeineren Methode kombinieren (oder sie zumindest an diese delegieren, um die Codewiederholung zu reduzieren):

WebElement getStaleElem(By by, WebDriver driver) {
    try {
        return driver.findElement(by);
    } catch (StaleElementReferenceException e) {
        System.out.println("Attempting to recover from StaleElementReferenceException ...");
        return getStaleElem(by, driver);
    } catch (NoSuchElementException ele) {
        System.out.println("Attempting to recover from NoSuchElementException ...");
        return getStaleElem(by, driver);
    }
}

Mit Java 7 würde sogar ein einzelner Multicatch-Block ausreichen:

WebElement getStaleElem(By by, WebDriver driver) {
    try {
        return driver.findElement(by);
    } catch (StaleElementReferenceException | NoSuchElementException e) {
        System.out.println("Attempting to recover from " + e.getClass().getSimpleName() + "...");
        return getStaleElem(by, driver);
    }
}

Auf diese Weise können Sie den Code, den Sie verwalten müssen, erheblich reduzieren.

41
Petr Janeček

Ich löse dies, indem Sie 1. das veraltete Element beibehalten und es abfragen, bis es eine Ausnahme auslöst, und dann 2. warten, bis das Element wieder sichtbar ist.

    boolean isStillOnOldPage = true;
    while (isStillOnOldPage) {
        try {
            theElement.getAttribute("whatever");
        } catch (StaleElementReferenceException e) {
            isStillOnOldPage = false;
        }
    }
    WebDriverWait wait = new WebDriverWait(driver, 15);
    wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("theElementId")));
1
Felix Dobslaw

Lösungen, um sie zu lösen:

  1. Locators in Ihren Elementen statt Referenzen speichern
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. Leverage-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. Deine Aktionen in die JavaScript-Injektion verschieben
 self.driver.execute_script("$(\"li:contains('Narendra')\").click()");
  1. Proaktiv warten, 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

0
NarendraC

Wenn Sie versuchen, auf den Link zu klicken, gelangen Sie zur neuen Seite. Danach zurück navigieren und auf andere Links klicken. Der folgende Code kann Ihnen helfen.

public int getNumberOfElementsFound(By by) {
    return  driver.findElements(by).size();
  }

public WebElement getElementWithIndex(By by, int pos) {
    return driver.findElements(by).get(pos);
  }

/**click on each link */
public void getLinks()throws Exception{
try {
List<WebElement> componentList = driver.findElements(By.tagName("a"));
System.out.println(componentList.size()); 

    for (WebElement component : componentList)
    {
        //click1();
        System.out.println(component.getAttribute("href"));
    }
 int numberOfElementsFound = getNumberOfElementsFound(By.tagName("a"));
for (int pos = 0; pos < numberOfElementsFound; pos++) {
     if (getElementWithIndex(By.tagName("a"), pos).isDisplayed()){

  getElementWithIndex(By.tagName("a"), pos).click();
  Thread.sleep(200);
  driver.navigate().back();
  Thread.sleep(200);                                                       
}
  }
    }catch (Exception e){
        System.out.println("error in getLinks "+e);
    }
}
0
Shammi

Für Fitnesse können Sie verwenden:

Starten Sie den Smart Web Driver Selen.Eigenschaften |

@Fixture (name = "Smart Web Driver") Public Klasse SmartWebDriver erweitert SlimWebDriver {

private final static Logger LOG = LoggerFactory.getLogger(SmartWebDriver.class);

/**
 * Constructs a new SmartWebDriver.
 */
@Start(name = "Start Smart Web Driver", arguments = {"configuration"}, example = "|start |Smart Web Driver| Selenium.properties|")
public SmartWebDriver(String configuration) {
    super(configuration);
}

/**
 * Waits for an element to become invisible (meaning visible and width and height != 0).
 *
 * @param locator the locator to use to find the element.
 */
@Command(name = "smartWaitForNotVisible", arguments = {"locator"}, example = "|smartWaitForNotVisible; |//path/to/input (of css=, id=, name=, classname=, link=, partiallink=)|")
public boolean smartWaitForNotVisible(String locator) {
    try {
        waitForNotVisible(locator);
    } catch (StaleElementReferenceException sere) {
        LOG.info("Element with locator '%s' did not become invisible (visible but also width and height != 0), a StaleElementReferenceException occurred, trying to continue...", locator);
    } catch (NoSuchElementException ele) {
        LOG.info("Element with locator '%s' did not become invisible (visible but also width and height != 0), a NoSuchElementException occurred, trying to continue...", locator);
    } catch (AssertionError ae) {
        if (ae.getMessage().contains("No element found")) {
            LOG.info("Element with locator '%s' did not become invisible (visible but also width and height != 0), a AssertionError occurred, trying to continue...", locator);
        } else {
            throw ae;
        }
    }
    return true;
}

}

0
Koenraad Appelo

Wenn eine Ausnahme für ein veraltetes Element auftritt !!

Ausnahmen für veraltete Elemente können auftreten, wenn sich die Bibliotheken, die diese Textboxen/Schaltflächen/Verknüpfungen unterstützen, geändert haben. Dies bedeutet, dass die Elemente gleich sind, sich jedoch der Verweis in der Website geändert hat, ohne die Locators zu beeinflussen. Daher ist die Referenz, die wir in unserem Cache gespeichert haben, einschließlich der Bibliotheksreferenz, alt oder veraltet, da die Seite mit aktualisierten Bibliotheken aktualisiert wurde.

for(int j=0; j<5;j++)
try {
    WebElement elementName=driver.findElement(By.xpath(“somexpath”));
    break;
} catch(StaleElementReferenceException e){
e.toString();
System.out.println(“Stale element error, trying ::  ” + e.getMessage());
}
elementName.sendKeys(“xyz”);
0
Nag Raj