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

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

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

【问题讨论】:

    标签: java testing selenium webdriver


    【解决方案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 语句会更清楚。
    • 我提出了一个解决方案,避免在这个答案stackoverflow.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)
               //...
              

              【讨论】:

                猜你喜欢
                相关资源
                最近更新 更多
                热门标签