我有一个关于“元素不再附加到DOM”的问题。
我尝试了不同的解决方案,但他们间歇性地工作。请建议一个永久性的解决方案。
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);
}
}
谢谢,Anu
您可能遇到的问题是该方法返回正确的(并且有效!)元素,但是当您稍后尝试访问它时,它会过时并抛出。
这通常出现在:
我知道有四种解决方法:
.isDisplayed()
方法或其他方法在找到它之后(在从方法返回之前)访问找到的元素。如果它抛出,你已经知道如何再次搜索。如果它通过,你还有一个(错误的)保证。WebElement
装饰器,记住它是如何被找到的,并在它被访问和抛出时重新找到它。这显然迫使你使用自定义的findElement()
方法来返回装饰器的实例(或者更好的是,装饰的WebDriver
将从通常的findElement()
和findElemens()
方法返回你的实例)。像这样做:
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
}
请注意,虽然我认为可以从通用WebElements访问foundBy
信息以使这更容易,但Selenium开发人员认为尝试这样的事情并选择not to make this information public是错误的。重新找到陈旧元素可能是一种不好的做法,因为你在没有任何机制检查它是否合理的情况下隐式重新找到元素。重新找到机制可能会找到一个完全不同的元素,而不是相同的元素。此外,当有很多找到的元素时,它会与findElements()
失败(你要么不允许重新找到findElements()
找到的元素,要么记住你的元素来自返回的List
的多少)。
我认为它有时是有用的,但是没有人会使用选项1和2,这显然是更好的解决方案,可以提高测试的稳健性。使用它们,只有在你确定需要它之后才能使用它们。@LoadsNewPage
,@Reversible
等注释每个任务。
你的大多数任务都会自己处理它们的异常,它们应该是独立的。
当队列遇到过时的元素异常时,它将从任务历史记录中获取最后一个任务并重新运行它以再次尝试。
这显然需要付出很多努力,如果不能很好地思考,可能会很快适得其反。在我手动修复它们所在的页面后,我使用了一个(更复杂和更强大)的变体来恢复失败的测试。在某些情况下(例如,在StaleElementException
上),失败不会立即结束测试,而是等待(在15秒后最终超时之前),弹出信息窗口并为用户提供手动刷新的选项页面/单击右键/修复表单/等等。然后,它将重新运行失败的任务,甚至可以在历史记录中返回一些步骤(例如,到最后一个@LoadsNewPage
工作)。总而言之,您的原始解决方案可以使用一些抛光。您可以将这两种方法合并为一种,更通用(或者至少使它们委托给这一种方法以减少代码重复):
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);
}
}
使用Java 7,即使是单个多块块就足够了:
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);
}
}
这样,您可以大大减少需要维护的代码量。
我通过以下方法解决这个问题:1。保持陈旧元素并轮询它直到它抛出异常,然后2.等待元素再次可见。
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")));
如果您尝试点击链接,则会转到新页面。之后导航回来并点击其他链接。他们下面的代码可以帮助你。
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);
}
}
解决它们的解决方案:
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))
这个解决方案对我有用
当发生陈旧元素异常时!!
支持这些文本框/按钮/链接的库已更改时,可能会发生陈旧元素异常,这意味着元素相同但现在在网站中更改了引用而不影响定位器。因此,我们存储在缓存中的引用(包括库引用)现在变得陈旧或陈旧,因为页面已使用更新的库刷新。
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”);
对于Fitnesse,您可以使用:
|开始|智能网络驱动程序| selenium.properties |
@Fixture(name =“Smart Web Driver”)公共类SmartWebDriver扩展了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;
}
}
https://www.swtestacademy.com/selenium-wait-javascript-angular-ajax/这是一篇关于动态服务员策略的好文章。您的问题是没有正确等待所有ajax,jquery或angular调用。然后你最终得到StaleElementException。
如果你的方法是使用Try-Catch机制,我想它有一个缺陷。你不应该依赖那个结构,因为你永远不会知道它会在catch子句中起作用。
Selenium让您有机会进行javascript调用。你可以执行
命令只是为了检查这些调用的存在和状态。
您可以在任何findBy操作之前执行此操作,以便始终使用最新页面