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 来查看一个实体是否出现在表中,然后我想对该行进行操作)。扭曲是我必须使用可以显示或隐藏列的动态表,所以我无法提前知道列的索引。所以首先我必须找到列的索引,然后获取带有值的我们通过 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)
然后以迭代方式继续解析
【讨论】:
应尽可能避免使用静态睡眠,因为它们会使测试变得脆弱 这是一个怎样的“解决方案”?el
是 WebElement
在检索它后可能已经过时,所以你 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的主要内容,如果未能解决你的问题,请参考以下文章
appium的webdriver和selenium有啥区别?
selenium之python源码解读-webdriver继承关系