随机"元素不再附加到DOM"StaleElementReferenceException

Ray*_*lus 137 java automated-tests webdriver selenium-webdriver

我希望它只是我,但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'"
Run Code Online (Sandbox Code Playgroud)

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

jar*_*rib 116

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

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

现在,在您单击元素的位置,元素引用不再有效.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();
Run Code Online (Sandbox Code Playgroud)

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);
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

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

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

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS)
Run Code Online (Sandbox Code Playgroud)

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

  • @jarib我不同意这一切都是由设计不佳的测试引起的.因为即使在元素出现在AJAX调用之后,仍然可能存在可能导致StaleElementReferenceException的jQuery代码.除了添加明显的等待似乎并不是很好之外,你无能为力.我认为这是WebDriver中的一个设计缺陷 (53认同)
  • 我是否正确地说,不再可能将元素读入变量并重新使用它们?因为我有一个巨大的干燥和动态的WATiR DSL依赖于传递元素而我正在尝试移植到webdriver,但我遇到了同样的问题.基本上我将不得不添加代码来重新读取模块中的所有元素,以用于改变DOM的每个测试步骤... (2认同)

aea*_*ron 10

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 你在这段代码中有一个错误,你不应该在没有某种上限的情况下递归调用方法,否则你就会把你的堆栈搞得一团糟. (7认同)
  • 我认为最好添加一个计数器或其他东西,所以当我们反复得到错误时,我们实际上可以抛出错误.否则,如果确实存在错误,您将最终处于循环中 (2认同)

Eer*_*ero 10

当AJAX更新处于中途时,我有时会收到此错误.Capybara似乎非常聪明地等待DOM更改(请参阅为什么wait_until已从Capybara中删除 ),但默认等待时间为2秒对我而言根本不够.在_spec_helper.rb_中更改为例如

Capybara.default_max_wait_time = 5
Run Code Online (Sandbox Code Playgroud)

  • 这也解决了我的问题:我得到了StaleElementReferenceError并且增加了Capybara.default_max_wait_time解决了这个问题. (2认同)