Java Selenium WebDriver如何解决陈旧元素引用异常?

Java Selenium WebDriver如何解决陈旧元素引用异常?,java,selenium,webdriver,selenium-webdriver,Java,Selenium,Webdriver,Selenium Webdriver,我在Selenium2Web驱动程序测试中有以下代码,在调试时可以使用,但在构建中运行时,大多数情况下都会失败。我知道这一定是与网页没有被刷新的方式有关,但不知道如何解决它,所以任何关于我做错了什么的指针都是值得赞赏的。我使用JSF primefaces作为我的web应用程序框架。单击“添加新链接”时,会出现一个弹出对话框,其中有一个输入框,我可以在其中输入日期,然后单击“保存”。在获取输入元素以输入文本时,我得到了一个陈旧的元素ref异常 提前谢谢 import static org.juni

我在Selenium2Web驱动程序测试中有以下代码,在调试时可以使用,但在构建中运行时,大多数情况下都会失败。我知道这一定是与网页没有被刷新的方式有关,但不知道如何解决它,所以任何关于我做错了什么的指针都是值得赞赏的。我使用JSF primefaces作为我的web应用程序框架。单击“添加新链接”时,会出现一个弹出对话框,其中有一个输入框,我可以在其中输入日期,然后单击“保存”。在获取输入元素以输入文本时,我得到了一个陈旧的元素ref异常

提前谢谢

import static org.junit.Assert.assertEquals;

 import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;


public class EnterActiveSubmissionIntegrationTest {
Map<String, Map<String, String>> tableData = new HashMap<String, Map<String, String>>();

@Test
public void testEnterActiveSubmission() throws Exception {
    // Create a new instance of the Firefox driver
    // Notice that the remainder of the code relies on the interface, 
    // not the implementation.
    System.setProperty("webdriver.chrome.driver", "C:/apps/chromedriver.exe");
    WebDriver driver = new ChromeDriver();

    // And now use this to visit Google
    driver.get("http://localhost:8080/strfingerprinting");
    // Alternatively the same thing can be done like this
    // driver.navigate().to("http://www.google.com");

    // Find the text input element by its name
    WebElement element = driver.findElement(By.linkText("Manage Submissions"));
    element.click();
    parseTableData(driver, "form:submissionDataTable_data", 1);
    assertEquals(tableData.get("form:submissionDataTable_data").get("12"), "Archived");

    WebElement newElement = driver.findElement(By.linkText("Add new"));
    newElement.click();

    WebDriverWait wait = new WebDriverWait(driver,10);
    wait.until(new ExpectedCondition<Boolean>() {
        public Boolean apply(WebDriver driver) {
            WebElement button = driver.findElement(By
                    .name("createForm:dateInput_input"));

            if (button.isDisplayed())
                return true;
            else
                return false;

        }
    });

    WebElement textElement = driver.findElement(By.name("createForm:dateInput_input"));
    textElement.sendKeys("24/04/2013");
    WebElement saveElement = driver.findElement(By.name("createForm:saveButton"));
    saveElement.click();

    driver.navigate().refresh();

    parseTableData(driver, "form:submissionDataTable_data", 2);

    //Close the browser
    driver.quit();
}



private void parseTableData(WebDriver driver, String id, int expectedRows) {
    // Check the title of the page or expected element on page
    WebElement subTableElement = driver.findElement(By.id(id));
    List<WebElement> tr_collection=subTableElement.findElements(By.xpath("id('"+ id + "')/tr"));

    assertEquals("incorrect number of rows returned", expectedRows, tr_collection.size());
    int row_num,col_num;
    row_num=1;

    if(tableData.get(id) == null) {
        tableData.put(id, new HashMap<String, String>());
    }
    Map<String, String> subTable = tableData.get(id);
    for(WebElement trElement : tr_collection)
    {
        List<WebElement> td_collection=trElement.findElements(By.xpath("td"));
        col_num=1;
        for(WebElement tdElement : td_collection)
        {
            subTable.put(row_num + "" + col_num, tdElement.getText());
            col_num++;
        }
        row_num++;
    }
}
}

异常跟踪

org.openqa.selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document
(Session info: chrome=26.0.1410.64)
  (Driver info: chromedriver=0.8,platform=Windows NT 6.0 SP2 x86) (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 56 milliseconds
For documentation on this error, please visit:        http://seleniumhq.org/exceptions/stale_element_reference.html
Build info: version: '2.32.0', revision: '6c40c187d01409a5dc3b7f8251859150c8af0bcb', time: '2013-04-09 10:39:28'
System info: os.name: 'Windows Vista', os.arch: 'x86', os.version: '6.0', java.version: '1.6.0_10'
Session ID: 784c53b99ad83c44d089fd04e9a42904
Driver info: org.openqa.selenium.chrome.ChromeDriver
Capabilities [{platform=XP, acceptSslCerts=true, javascriptEnabled=true,   browserName=chrome, rotatable=false, driverVersion=0.8, locationContextEnabled=true,  version=26.0.1410.64, cssSelectorsEnabled=true, databaseEnabled=true, handlesAlerts=true,  browserConnectionEnabled=false, nativeEvents=true, webStorageEnabled=true,   applicationCacheEnabled=false, takesScreenshot=true}]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at  sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at  sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.openqa.selenium.remote.ErrorHandler.createThrowable(ErrorHandler.java:187)
at org.openqa.selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.java:145)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:554)
at org.openqa.selenium.remote.RemoteWebElement.execute(RemoteWebElement.java:268)
at org.openqa.selenium.remote.RemoteWebElement.isDisplayed(RemoteWebElement.java:320)
at com.integration.web.EnterActiveSubmissionIntegrationTest$1.apply(EnterActiveSubmissionIntegrationTest.java:58)
at com.integration.web.EnterActiveSubmissionIntegrationTest$1.apply(EnterActiveSubmissionIntegrationTest.java:1)
at org.openqa.selenium.support.ui.FluentWait.until(FluentWait.java:208)
at com.integration.web.EnterActiveSubmissionIntegrationTest.testEnterActiveSubmission(EnterActiveSubmissionIntegrationTest.java:53)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

StaleElementReferenceException是由于findelement方法访问的元素不可用造成的

在对元素执行任何操作之前,您需要确保(如果您对该元素的可用性有疑问)

等待元素的可见性

(new WebDriverWait(driver, 10)).until(new ExpectedCondition()
    {
           public Boolean apply(WebDriver d) {
              return d.findElement(By.name("createForm:dateInput_input")).isDisplayed();
     }});

或者使用逻辑来验证元素是否存在。

首先,让我们弄清楚什么是WebElement

WebElement是对DOM中元素的引用

当您正在交互的元素被销毁然后重新创建时,将引发StaleElementException。现在,大多数复杂的网页都会在用户与之交互时动态移动,这需要销毁和重新创建DOM中的元素


当这种情况发生时,对DOM中以前拥有的元素的引用将变得过时,并且您不再能够使用此引用与DOM中的元素交互。发生这种情况时,您需要刷新引用,或者在现实世界中再次查找元素。

这不是问题。如果将.findElement调用包装在try-catch块中并捕获StaleElementReferenceException,则可以根据需要循环并重试多次,直到成功

给你

项目的另一个例子:


使用Selenium提供的预期条件等待WebElement

调试时,客户机的速度不如您只运行单元测试或maven构建。 这意味着在调试模式下,客户机有更多的时间准备元素,但是如果构建运行的是相同的代码,那么他会更快,并且您要查找的WebElement可能在页面的DOM中不可见

相信我,我也有同样的问题

例如:

inClient.waitUntil(ExpectedConditions.visibilityOf(YourElement,2000))
这个简单的方法调用在他调用后等待2秒钟,以查看DOM上WebElement的可见性。

这对我来说很有效(来源):

/**
*尝试多次单击某个元素(以避免过时的元素
*由快速DOM刷新引起的异常)
*
*@param d
*网络驱动程序
*@param by
*按元素定位器
*/
公共静态无效可靠单击(WebDriver d,By)
{
最终整数最大等待时间=10;
最终整数最大值元素重试次数=5;
WebDriverWait wait=新的WebDriverWait(d,最大等待时间);
int重试次数=0;
while(true)
{
尝试
{
wait.until(ExpectedConditions.elementToBeClickable(by))。单击();
返回;
}
捕获(StaleElementReferenceException e)
{
如果(重试次数
WebDriver必须等待元素找到并且在10秒后超时

WebElement myDynamicElement1 = new WebDriverWait(driver, 10).until(
    ExpectedConditions.presenceOfElementLocated(
        By.name("createForm:dateInput_input")
    )
);

请尝试等待以下元素:

// Waiting 30 seconds for an element to be present on the page, checking
// for its presence once every 5 seconds.
Wait<WebDriver> stubbornWait = new FluentWait<WebDriver>(driver)
    .withTimeout(30, SECONDS)
    .pollingEvery(5, SECONDS)
    .ignoring(NoSuchElementException.class)
    .ignoring(StaleElementReferenceException.class);

WebElement foo = stubbornWait.until(new Function<WebDriver, WebElement>() {
    public WebElement apply(WebDriver driver) {
        return driver.findElement(By.id("foo"));
    }
});
//等待30秒,让元素出现在页面上,检查
//每5秒检查一次。
等待等待=新建FluentWait(驱动程序)
.带超时(30秒)
.每(5秒)轮询一次
.忽略(NoSuchElementException.class)
.忽略(StaleElementReferenceException.class);
WebElement foo=wait.until(新函数(){
公共WebElement应用(WebDriver){
返回驱动程序findElement(By.id(“foo”);
}
});

发生在我身上的事情是webdriver会找到对DOM元素的引用,然后在获得该引用后的某个时刻,javascript会删除该元素并重新添加它(因为页面基本上是在重画)

试试这个。找出导致dom元素从dom中移除的操作。在我的例子中,这是一个异步ajax调用,当ajax调用完成时,元素将从DOM中删除。在该操作之后,等待元素过时:

... do a thing, possibly async, that should remove the element from the DOM ...
wait.until(ExpectedConditions.stalenessOf(theElement));
此时,您可以确定元素现在已过时。因此,下次引用该元素时,请再次等待,这次等待将其重新添加到DOM中:

wait.until(ExpectedConditions.presenceOfElementLocated(By.id("whatever")))

我建议不要对Selenium WebDriver使用
@CachelookUp
,用于
StaleElementReferenceException


如果您使用的是
@FindBy
注释,并且具有
@CacheLookUp
,请将其注释出来并检查。

如果我们对答案不确定,请不要混淆他人。这对最终用户来说是相当令人沮丧的。简单而简短的答案是 在webdriver中使用@CacheLookup注释。请参考下面的链接。
我用以下代码解决了这个问题

public WebElement waitForElement(final By findBy, final int waitTime) {
    Wait<AppiumDriver> wait = new FluentWait<>((AppiumDriver) driver)
            .withTimeout(waitTime, TimeUnit.SECONDS)
            .pollingEvery(POLL_TIME, TimeUnit.SECONDS)
            .ignoring(NoSuchElementException.class,StaleElementReferenceException.class);

    WebElement webElement = wait.until(new Function<AppiumDriver, WebElement>() {
        @Override
        public WebElement apply(AppiumDriver driver) {
            System.out.println("Trying to find element " + findBy.toString());                
            WebElement element = driver.findElement(findBy);
            return element;
        }
    });
    return webElement;
}
公共WebElement waitForElement(final By findBy,final int waitTime){
Wait Wait=new FluentWait((AppiumDriver)驱动程序)
.withTimeout(等待时间,时间单位为秒)
.pollingEvery(轮询时间,时间单位为秒)
.忽略(NoSuchElementException.class,StaleElementReferenceException.class);
WebElement WebElement=等待.until(新函数
// Waiting 30 seconds for an element to be present on the page, checking
// for its presence once every 5 seconds.
Wait<WebDriver> stubbornWait = new FluentWait<WebDriver>(driver)
    .withTimeout(30, SECONDS)
    .pollingEvery(5, SECONDS)
    .ignoring(NoSuchElementException.class)
    .ignoring(StaleElementReferenceException.class);

WebElement foo = stubbornWait.until(new Function<WebDriver, WebElement>() {
    public WebElement apply(WebDriver driver) {
        return driver.findElement(By.id("foo"));
    }
});
... do a thing, possibly async, that should remove the element from the DOM ...
wait.until(ExpectedConditions.stalenessOf(theElement));
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("whatever")))
public WebElement waitForElement(final By findBy, final int waitTime) {
    Wait<AppiumDriver> wait = new FluentWait<>((AppiumDriver) driver)
            .withTimeout(waitTime, TimeUnit.SECONDS)
            .pollingEvery(POLL_TIME, TimeUnit.SECONDS)
            .ignoring(NoSuchElementException.class,StaleElementReferenceException.class);

    WebElement webElement = wait.until(new Function<AppiumDriver, WebElement>() {
        @Override
        public WebElement apply(AppiumDriver driver) {
            System.out.println("Trying to find element " + findBy.toString());                
            WebElement element = driver.findElement(findBy);
            return element;
        }
    });
    return webElement;
}
for i in range(4):
    try:
        element = WebDriverWait(driver, 120).until( \
                EC.presence_of_element_located((By.XPATH, 'xpath')))
        element.click()    
        break
    except StaleElementReferenceException:
        print "exception "
public void inputName(String name)
{
    try {
        waitForVisibilityElement(name);//My own visibility function
        findElement(By.name("customerName")).sendKeys(name);
    }
    catch (StaleElementReferenceException e)
    {
        e.getMessage();
    }
}
var pollLoop = function () {
      element(by.id('spinnerElem')).getAttribute('class').then(function (cls) {
                if (cls.indexOf('spinner-active') > -1) {
                    // wait for the spinner
                } else {
                    //do your logic
                    promise.defer().fulfill();
                }
            }, function () {
                // This err handling function is to handle the {StaleElementReferenceError} and makes sure we find the element always.
                pollLoop();
            });
        };
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))
<div class="modal-content" uib-modal-transclude="">
    <div class="modal-header">
        ...
    </div>
    <div class="modal-body">
        <form class="form-horizontal ...">
            ...
        </form>
    <div>
<div>
def parse(self, response):
class MySpider(scrapy.Spider):
     action_chains = ActionChains(self.driver)
def parse(self, response):
     self.driver.get(response.url)
     action_chains = ActionChains(self.driver)
    //Search button
    @FindBy(how=How.XPATH, using =".//input[@value='Search']")  
    //@CachelookUp
    WebElement BtnSearch;
for(int j=0; j<5;j++)
try {
    WebElement elementName=driver.findElement(By.name(“createForm:dateInput_input”));
    break;
} catch(StaleElementReferenceException e){
e.toString();
System.out.println(“Stale element error, trying ::  ” + e.getMessage());
}
elementName.sendKeys(“20/06/2018”);