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
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:
Es gibt vier Lösungsmöglichkeiten, die ich kenne:
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.
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.
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.
Implementieren Sie Ihren gesamten Workflow auf neue Weise!
@LoadsNewPage
, @Reversible
usw.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).
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.
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")));
Lösungen, um sie zu lösen:
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
# 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)
self.driver.execute_script("$(\"li:contains('Narendra')\").click()");
# 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
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);
}
}
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;
}
}
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”);