随机“元素不再附加到DOM”StaleElementReferenceException

问题

我希望它只是我,但Selenium Webdriver似乎是一场彻头彻尾的噩梦。 Chrome webdriver目前无法使用,而其他驱动程序则非常不可靠,或者看起来如此。我正在与许多问题作斗争,但这里有一个问题。

随机地,我的测试将失败

"org.openqa.selenium.StaleElementReferenceException: Element is no longer attached 
to the DOM    
System info: os.name: 'Windows 7', os.arch: 'amd64',
 os.version: '6.1', java.version: '1.6.0_23'"

我正在使用webdriver版本2.0b3。我已经看到FF和IE驱动程序发生这种情况。我可以阻止这种情况的唯一方法是在发生异常之前添加实际调用Thread.sleep。这是一个糟糕的解决方法,所以我希望有人可以指出我的错误,这将使这一切变得更好。


#1 热门回答(111 赞)

是的,如果你遇到StaleElementReferenceExceptions问题,那是因为你的测试编写得很糟糕。这是一场竞争。请考虑以下情形:

WebElement element = driver.findElement(By.id("foo"));
// DOM changes - page is refreshed, or element is removed and re-added
element.click();

现在,在你单击元素的位置,元素引用不再有效。 WebDriver几乎不可能对可能发生这种情况的所有情况做出很好的猜测 - 所以它会抛出手并控制你,因为测试/应用程序作者应该确切知道可能会发生什么或不会发生什么。你要做的是明确等待,直到DOM处于你不知道事情不会改变的状态。例如,使用WebDriverWait等待特定元素存在:

// times out after 5 seconds
WebDriverWait wait = new WebDriverWait(driver, 5);

// while the following loop runs, the DOM changes - 
// page is refreshed, or element is removed and re-added
wait.until(presenceOfElementLocated(By.id("container-element")));        

// now we're good - let's click the element
driver.findElement(By.id("foo")).click();

presenceOfElementLocated()方法看起来像这样:

private static Function<WebDriver,WebElement> presenceOfElementLocated(final By locator) {
    return new Function<WebDriver, WebElement>() {
        @Override
        public WebElement apply(WebDriver driver) {
            return driver.findElement(locator);
        }
    };
}

你对目前的Chrome驱动程序非常不稳定感到非常正确,你会很高兴听到Selenium主干有一个重写的Chrome驱动程序,其中大部分实现都是由Chromium开发人员完成的。

PS。或者,你可以启用隐式等待,而不是像上面的示例中那样显式等待 - 这样WebDriver将始终循环直到指定的超时等待元素出现:

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS)

根据我的经验,明确等待总是更可靠。


#2 热门回答(9 赞)

我已经能够使用这样的方法取得了一些成功:

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

是的,它只是继续轮询元素,直到它不再被认为陈旧(新鲜?)。并没有真正找到问题的根源,但我发现WebDriver可能会相当挑剔抛出这个异常 - 有时候我会得到它,有时候我没有。或者可能是DOM真的在变化。

所以我不完全同意上面的答案,这必然表明一个写得不好的测试。我已经把它放在新页面上,我没有以任何方式与之互动。我认为在DOM的表示方式或者WebDriver认为过时的方式都存在一些瑕疵。


#3 热门回答(7 赞)

当AJAX更新处于中途时,我有时会收到此错误。 Capybara似乎非常聪明地等待DOM更改(参见Why wait_until was removed from Capybara),但在我的情况下,默认的2秒等待时间是不够的。已更改_spec_helper.rb_,例如

Capybara.default_wait_time = 5