Selenium WebDriver StaleElementReferenceException

Posted

技术标签:

【中文标题】Selenium WebDriver StaleElementReferenceException【英文标题】: 【发布时间】:2011-06-18 07:15:23 【问题描述】:

运行测试时出现此错误: org.openqa.selenium.StaleElementReferenceException: 元素不再附加到 DOM

关于如何解决上述异常的任何想法? 这发生在我的网格中 它有一个动态的 ref Xpath 表达式

【问题讨论】:

【参考方案1】:

当您尝试使用页面上不再存在的 WebElement 的方法时,将引发该异常。如果您的网格正在动态加载数据并且您刷新了网格,则对该网格上元素的任何引用都将是“陈旧的”。仔细检查您尝试引用的元素是否在测试页面上,您可能需要重新实例化该对象。

【讨论】:

我遇到这个问题的情况是这样的: - 等到表格单元格中有一个值,在标题'someHeader'下,对于表格'tableLocator',如果找到返回其行索引。 (我试图通过查找它的 ID 来查看一个实体是否出现在表中,然后我想对该行进行操作)。扭曲是我必须使用可以显示或隐藏列的动态表,所以我无法提前知道列的索引。所以首先我必须找到列的索引,然后获取带有值的 s。 我在这一行得到错误 _genderDropdown = new Select( _driver.findElement( By.xpath( String.format( SELECT_COLUMN_VALUE_LOCATOR, _rowNumber, GENDER_COLUMN ) ) ) );其中 SELECT_COLUMN_VALUE_LOCATOR 的 xpath = public static final String SELECT_COLUMN_VALUE_LOCATOR = "//table/tbody/tr[%d]/td[%d]/div/select"; 【参考方案2】:

我们通过 WebdriverWrapper 和 WebElementWrapper 来解决这个问题。

这些包装器所做的是处理其中的 StaleElementException,然后使用定位器重新评估并获取新的 WebElement 对象。这样,您需要将处理异常的代码分布在整个代码库中,并将其本地化为一个类。

我会尽快研究这几个课程的开源,如果你们感兴趣的话,我会在此处添加一个链接。

【讨论】:

如果可以,请添加链接。 @Pavan Sudarshan:你有链接吗? 请问有链接吗? 这里有一个例子yesterdayseggs.com/… github.com/itspanzi/WaitUtils - 这是我从上述讨论中外化的小库。我没有让它特定于 StaleElementException,而是让它在等待特定状态/谓词方面更加通用。【参考方案3】:

我遇到了同样的问题,但找不到任何解决方案。想出了一个解决方案并将其发布在这里,希望这可以帮助遇到同样问题的人。我创建了一个类来根据它们的类型、cssselector、id 等处理陈旧的元素,并像调用任何其他页面对象一样简单地调用它。

public void StaleElementHandleByID (String elementID)

    int count = 0;
    boolean clicked = false;
    while (count < 4 && !clicked)
    
        try 
        
            WebElement yourSlipperyElement= driver.findElement(By.id(elementID));
            yourSlipperyElement.click(); 
            clicked = true;
         
        catch (StaleElementReferenceException e)
        
            e.toString();
            System.out.println("Trying to recover from a stale element :" + e.getMessage());
            count = count+1;
        
    

我建议仅在您知道会导致 WebDriver 出现问题的元素上使用它。

【讨论】:

我不太喜欢这个解决方案,因为每次迭代都可能会超时,而无法找到元素。通常最好创建一个更精确的定位器来查找新的/更新的元素,而不是旧的/陈旧的元素。 我昨天遇到了这个问题,因为我遇到了类似的问题。将相关代码包装在 try/catch 块中,然后重试它对我有用。但令我惊讶的是,没有人指出上述代码中的缺陷。它只会重复一次,因为即使执行了 catch 块,也会执行 count = count+4 行。我认为这个想法可能是在 yourSlipperyElement.click(); 之后将该行包含在 try 块中。这样只有在没有抛出异常时才会执行。 @MartinMcCallion 我认为这就是想法 - 如果点击成功,您无需重试。一个 break 语句会更清楚。 我提出了一个解决方案,避免在这个答案***.com/a/25470347/34859 中编写所有这些重试逻辑 @Kenny 我的意思是,即使捕获到异常,+4 也会发生,因此重试永远不会发生。如果 +4 在 yourSlipperyElement.click(); 行之后,那么它最多会重试四次,我认为这是本意。【参考方案4】:

它也遇到了这个问题,看起来很明显模态面板加载陷入了竞争状态并一直等待到超时。

我尝试了很多次,发现解决办法是保持模态面板加载,直到它可以被webDriver准确找到,同时保持刷新webDriver实例,然后尝试在模态面板中找到WebElements。

所以解决方法如下: 例如MyModalPanel 是您的 ModalPanel Id,然后执行以下操作

page.openModalPanel();
Assert.assertTrue(page.waitTillDisplay( "MyModalPanelContentDiv"), Wait.MODAL_PANEL));
page.fillInFormInModalpanel(formObj);

waitTillDisplay 代码可以在 WebDriver 网站上找到,我只是将我的代码粘贴在这里供您参考:

public Boolean waitTillDisplay(final String id, int waitSeconds)

    WebDriverWait wait = new WebDriverWait(driver, waitSeconds);
        Boolean displayed = wait.until(new ExpectedCondition<Boolean>() 
              public Boolean apply(WebDriver driver) 
                  return driver.findElement(By.id(id)).isDisplayed();
              

        );
        return displayed;

【讨论】:

【参考方案5】:

为了更加灵活,我做了一些更改:

   delegate void StaleFunction(IWebElement elt);
        private static void StaleElementHandleByID(By by, StaleFunction func )
        
            int count = 0;
            while (count < 4)
            
                try
                
                    IWebElement yourSlipperyElement = Driver.FindElement(by);
                    func(yourSlipperyElement);
                    count = count + 4;
                
                catch (StaleElementReferenceException e)
                
                    count = count + 1;
                

            
        

 StaleElementHandleByID(By.Id("identDdl"),
                    delegate(IWebElement elt)
                
                    SelectElement select = new SelectElement(elt);
                    select.SelectByText(tosave.ItemEquipamentoCem.CodigoCne.ToString());
                );

【讨论】:

【参考方案6】:

快速而肮脏的解决方案:

el.click()

time.sleep(1)

然后以迭代方式继续解析

【讨论】:

应尽可能避免使用静态睡眠,因为它们会使测试变得脆弱 这是一个怎样的“解决方案”? elWebElement 在检索它后可能已经过时,所以你 click() 它会抛出 StaleElementReferenceException(所以你的 time.sleep(1),不管它是什么,甚至都没有被执行),然后? 【参考方案7】:

您可能会在单击元素后尝试获取任何元素属性。

我遇到了同样的问题,我试图在单击按钮后获取按钮的 getText()。就我而言,一旦单击按钮,就会出现新窗口。

【讨论】:

【参考方案8】:

我使用了 FluentWait 和 ExpectedCondition 应用覆盖:https://gist.github.com/djangofan/5112655。这个处理最终块内的异常,与其他人回答这个问题的方式不同,并允许将连续尝试包装在该块中。我觉得这样更优雅。

public static void clickByLocator( final By locator ) 
  final long startTime = System.currentTimeMillis();
  driver.manage().timeouts().implicitlyWait( 5, TimeUnit.SECONDS );
  Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
        .withTimeout(90000, TimeUnit.MILLISECONDS)
        .pollingEvery(5500, TimeUnit.MILLISECONDS);
        //.ignoring( StaleElementReferenceException.class );        
  wait.until( new ExpectedCondition<Boolean>()  
    @Override 
    public Boolean apply( WebDriver webDriver ) 
      try 
        webDriver.findElement( locator ).click();
        return true;
       catch ( StaleElementReferenceException e )                       // try again
        return false;
           
     
   );      
  driver.manage().timeouts().implicitlyWait( DEFAULT_IMPLICIT_WAIT, TimeUnit.SECONDS );
  long endTime   = System.currentTimeMillis();
  long totalTime = endTime - startTime;
  log("Finished click after waiting for " + totalTime + " milliseconds.");

【讨论】:

嗨,我检查了你在这里提到的页面,你的函数只是返回元素,但总是有可能点击返回的元素可能会再次抛出相同的异常......例如 method4 和 method5 on您的页面返回 webelement,如果调用方法单击此元素并抛出 StateElementReferenceException..我在这里遗漏了什么吗?? 按照上面的写法,我认为它不会抛出 StaleElementReferenceException,因为我吞下了错误并返回了布尔值。 我指的是您发布的链接上的method4和method5。在上面的答案中,我可以看到您正在捕获异常,但是返回 webelement 的方法呢? 对不起。这些只是想法/想法。不一定经过测试。您的里程可能会有所不同。您可以从该链接中汲取灵感,但实际上您最终可能会得到自己的解决方案。【参考方案9】:

在这种情况下,测试正在寻找尚未加载或已刷新的元素。因此,StaleElementException。一个简单的解决方案是添加 fluentWait。

【讨论】:

【参考方案10】:
public static Boolean executeElementSendKeys
(WebDriver driver, WebElement element, String sInputParameters) throws Exception 
    return (Boolean) executeElementMaster
            (driver, element, "sendKeys", sInputParameters, 30, true);


public static Boolean executeElementClear
(WebDriver driver, WebElement element) throws Exception 
    return (Boolean) executeElementMaster (driver, element, "clear", "", 30, true);


public static String executeElementGetText
(WebDriver driver, WebElement element) throws Exception 
    return (String) executeElementMaster (driver, element, "getText", "", 30, true);


public static Boolean executeElementClick
(WebDriver driver, WebElement element) throws Exception 
    return (Boolean) executeElementMaster (driver, element, "click", "", 30, true);


public static boolean executeElementIsDisplayed
(WebDriver driver, WebElement element) throws Exception 
    return (Boolean) executeElementMaster (driver, element, "isDisplayed", "", 30, true);


public static String executeElementGetAttribute
(WebDriver driver, WebElement element, String sInputParameters) throws Exception 
    return (String) executeElementMaster
            (driver, element, "getAttribute", sInputParameters, 30, true);

// 下面是处理StaleElementReferenceException 和其他异常的主方法。

// 如果您希望此方法仅针对 StaleElementReferenceException 而不是其他异常重试操作(如单击、发送键等),则在 catch 部分中,将 (Exception e) 替换为 (StaleElementReferenceException e)

private static Object executeElementMaster(WebDriver driver, WebElement element, String sExecuteAction, String sInputParametersOptional, int MaxTimeToWait,
        boolean bExpectedElementState) throws Exception 
    try 
        // Local variable declaration
        String sElementString = "";
        String sElementXpath = "";
        Object ReturnValue = "";
        int Index = 0;
        boolean bCurrentElementState = true;
        boolean bWebDriverWaitUntilElementClickableFlag = false;

        System.out.println("**** Execute method '" + sExecuteAction + "' on '" + sElementString + "' - Expected : '" + bExpectedElementState + "'");
        System.out.println("**** MaxTimeToWait ='" + MaxTimeToWait + "' seconds");

        // Set browser timeout to 1 second. Will be reset to default later
        driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);

        // Keep trying until 'MaxTimeToWait' is reached 
        for (int i = 0; i < MaxTimeToWait; i++) 
            try 
                // Get element xPath - and find element again
                if (element != null && i < 2 && sElementString == "") 
                    sElementString = (element).toString();
                    if (sElementString.contains("xpath: ")) 
                        // Retrieve xPath from element, if available
                        Index = sElementString.indexOf("xpath: ");
                        sElementXpath = sElementString.substring(Index + 7, sElementString.length());
                    
                

                // Find Element again
                if (sElementXpath != "" && i > 0) 
                    element = driver.findElement(By.xpath(sElementXpath));
                

                // Execute the action requested
                switch (sExecuteAction) 
                    case ("isDisplayed"):
                        // Check if element is displayed and save in bCurrentElementState variable
                        ReturnValue = element.isDisplayed();
                        bCurrentElementState = (Boolean) ReturnValue;
                        bWebDriverWaitUntilElementClickableFlag = true;
                        break;
                    case ("getText"):
                        ReturnValue = element.getText();
                        bCurrentElementState = true;
                        bWebDriverWaitUntilElementClickableFlag = false;
                        break;
                    case ("sendKeys"):
                        // Scroll element into view before performing any action

                        element.sendKeys(sInputParametersOptional);
                        ReturnValue = true;
                        bCurrentElementState = true;
                        bWebDriverWaitUntilElementClickableFlag = false;
                        break;
                    case ("clear"):
                        // Scroll element into view before performing any action

                        element.clear();
                        ReturnValue = true;
                        bCurrentElementState = true;
                        bWebDriverWaitUntilElementClickableFlag = false;
                        break;
                    case ("click"):
                        // Scroll element into view before performing any action

                        element.click();
                        ReturnValue = true;
                        bCurrentElementState = true;
                        bWebDriverWaitUntilElementClickableFlag = false;
                        break;
                    default:
                        ReturnValue = element.getAttribute(sInputParametersOptional);
                        bCurrentElementState = true;
                        break;
                
             catch (Exception e) 
                Thread.sleep(500);
                bCurrentElementState = false;
                ReturnValue = false;
            
            if (bCurrentElementState == bExpectedElementState) 
                // If element's actual and expected states match, log result and return value
                System.out.println("**** PASSED: Execute method '" + sExecuteAction + "' on '" + sElementString + "' - Returned '" + ReturnValue + "' ****   \n"
                        + "Actual element status: '" + bCurrentElementState + "'  Expected element status: '" + bExpectedElementState + "'");
                break;
             else 
                // If element's actual and expected states do NOT match, loop until they match or timeout is reached
                Thread.sleep(500);
            
        
        // Reset browser timeout to default
        driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
        // Return value before exiting
        if (bCurrentElementState != bExpectedElementState) 
            // If element's actual and expected states do NOT match, log result and return value
            System.out.println("**** FAILED: Execute method '" + sExecuteAction + "' on '" + sElementString + "' - Returned '" + ReturnValue + "' ****   \n"
                    + "Actual element status: '" + bCurrentElementState + "'  Expected element status: '" + bExpectedElementState + "'");
            if (sExecuteAction.equalsIgnoreCase("findElement")) 
                ReturnValue = null;
            
        

        return ReturnValue;
     catch (Exception e) 
        System.out.println("Exception in executeElementMaster - " + e.getMessage());
        throw (e);
    

【讨论】:

【参考方案11】:

@netz75:谢谢。当点击重定向到第二页时出现此问题。 这对我有用:

 //.. (first page asserts)
 //...
 element.Click();   
 Thread.Sleep(200);
 //.. (second page asserts)
 //...

【讨论】:

以上是关于Selenium WebDriver StaleElementReferenceException的主要内容,如果未能解决你的问题,请参考以下文章

Selenium Webdriver概述

appium的webdriver和selenium有啥区别?

selenium之python源码解读-webdriver继承关系

From  selenium  import  webdriver

Selenium WebDriver(Python)API

selenium+python - webdriver​模拟键盘ENTER没有效果