如何避免在Selenium中的“StaleElementReferenceException”?
我正在使用Java实施大量的Seleniumtesting。 有时,由于StaleElementReferenceException
,我的testing失败。 你能提出一些方法来使testing更稳定吗?
如果页面上发生的DOM操作暂时导致元素无法访问,就会发生这种情况。 为了允许这些情况,可以尝试在循环中多次访问元素,然后才抛出exception。
看到这个出色的解决
http://darrellgrainger.blogspot.com/2012/06/staleelementexception.html
我一直在间歇地处理这个问题。 不知道的是,BackboneJS正在页面上运行,取代了我试图点击的元素。 我的代码看起来像这样。
driver.findElement(By.id("checkoutLink")).click();
这当然在function上与此相同。
WebElement checkoutLink = driver.findElement(By.id("checkoutLink")); checkoutLink.click();
偶尔会发生什么是javascript将取代检查和点击它之间的checkoutLink元素,即。
WebElement checkoutLink = driver.findElement(By.id("checkoutLink")); // javascript replaces checkoutLink checkoutLink.click();
当试图点击链接时,会导致StaleElementReferenceException。 我找不到任何可靠的方法来告诉WebDriver,等待JavaScript完成运行,所以这是我最终如何解决它。
new WebDriverWait(driver, timeout) .ignoring(StaleElementReferenceException.class) .until(new Predicate<WebDriver>() { @Override public boolean apply(@Nullable WebDriver driver) { driver.findElement(By.id("checkoutLink")).click(); return true; } });
此代码将不断尝试点击链接,忽略StaleElementReferenceExceptions直到点击成功或达到超时。 我喜欢这个解决scheme,因为它不需要编写任何重试逻辑,而只使用WebDriver的内置结构。
通常这是由于DOM正在更新,你试图访问一个更新的/新的元素 – 但DOM的刷新,所以这是一个无效的参考,你有..
首先在元素上使用明确的等待来确保更新完成,然后重新获取元素的新引用。
这里有一些伪代码来说明(从我使用的一些C#代码改编为这个问题):
WebDriverWait wait = new WebDriverWait(browser, TimeSpan.FromSeconds(10)); IWebElement aRow = browser.FindElement(By.XPath(SOME XPATH HERE); IWebElement editLink = aRow.FindElement(By.LinkText("Edit")); //this Click causes an AJAX call editLink.Click(); //must first wait for the call to complete wait.Until(ExpectedConditions.ElementExists(By.XPath(SOME XPATH HERE)); //you've lost the reference to the row; you must grab it again. aRow = browser.FindElement(By.XPath(SOME XPATH HERE); //now proceed with asserts or other actions.
希望这可以帮助!
肯尼的解决scheme是好的,但它可以用更优雅的方式写出来
new WebDriverWait(driver, timeout) .ignoring(StaleElementReferenceException.class) .until((WebDriver d) -> { d.findElement(By.id("checkoutLink")).click(); return true; });
还有:
new WebDriverWait(driver, timeout).ignoring(StaleElementReferenceException.class).until(ExpectedConditions.elementToBeClickable(By.id("checkoutLink"))); driver.findElement(By.id("checkoutLink")).click();
C#中的解决scheme将是:
帮手类:
internal class DriverHelper { private IWebDriver Driver { get; set; } private WebDriverWait Wait { get; set; } public DriverHelper(string driverUrl, int timeoutInSeconds) { Driver = new ChromeDriver(); Driver.Url = driverUrl; Wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(timeoutInSeconds)); } internal bool ClickElement(string cssSelector) { //Find the element IWebElement element = Wait.Until(d=>ExpectedConditions.ElementIsVisible(By.CssSelector(cssSelector)))(Driver); return Wait.Until(c => ClickElement(element, cssSelector)); } private bool ClickElement(IWebElement element, string cssSelector) { try { //Check if element is still included in the dom //If the element has changed a the OpenQA.Selenium.StaleElementReferenceException is thrown. bool isDisplayed = element.Displayed; element.Click(); return true; } catch (StaleElementReferenceException) { //wait until the element is visible again element = Wait.Until(d => ExpectedConditions.ElementIsVisible(By.CssSelector(cssSelector)))(Driver); return ClickElement(element, cssSelector); } catch (Exception) { return false; } } }
调用:
DriverHelper driverHelper = new DriverHelper("http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp", 10); driverHelper.ClickElement("input[value='csharp']:first-child");
同样可以用于Java。
StaleElementReferenceException
发生的原因已经被列出:在元素之间查找和做某事之间更新DOM。
对于点击问题,我最近使用了这样的解决scheme:
public void clickOn(By locator, WebDriver driver, int timeout) { final WebDriverWait wait = new WebDriverWait(driver, timeout); wait.until(ExpectedConditions.refreshed( ExpectedConditions.elementToBeClickable(locator))); driver.findElement(locator).click(); }
关键部分是Selenium通过ExpectedConditions.refreshed()
自己的ExpectedConditions
的“链接”。 这实际上会等待并检查在指定的超时期间是否刷新了元素,并等待元素变为可点击。
查看刷新方法的文档 。
在我的项目中,我介绍了StableWebElement的概念。 它是WebElement的一个包装器,它能够检测元素是否陈旧,并find对原始元素的新引用。 我添加了一个辅助方法来定位返回StableWebElement而不是WebElement的元素,并且StaleElementReference的问题消失了。
public static IStableWebElement FindStableElement(this ISearchContext context, By by) { var element = context.FindElement(by); return new StableWebElement(context, element, by, SearchApproachType.First); }
在C#中的代码可以在我的项目的页面上,但它可以很容易地移植到Java https://github.com/cezarypiatek/Tellurium/blob/master/Src/MvcPages/SeleniumUtils/StableWebElement.cs
也许它是最近添加的,但是其他的答案没有提到Selenium的隐含的等待function,它为你做了以上所有的工作,并被内置到Selenium中。
driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);
这将重试findElement()
调用,直到find元素,或者10秒钟。
来源 – http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp