2012-10-19 36 views

Trả lời

44

Điều này có thể xảy ra nếu hoạt động DOM xảy ra trên trang tạm thời khiến cho yếu tố không thể truy cập được. Để cho phép những trường hợp đó, bạn có thể thử truy cập phần tử vài lần trong một vòng lặp trước khi cuối cùng ném một ngoại lệ.

Hãy thử this excellent solution from darrelgrainger.blogspot.com:

public boolean retryingFindClick(By by) { 
    boolean result = false; 
    int attempts = 0; 
    while(attempts < 2) { 
     try { 
      driver.findElement(by).click(); 
      result = true; 
      break; 
     } catch(StaleElementException e) { 
     } 
     attempts++; 
    } 
    return result; 
} 
+0

Wow! Đây chỉ là những gì tôi cần. Cảm ơn! – SpartaSixZero

+0

Nó cũng có thể được sửa bằng cách sử dụng tham chiếu khác nhau của phần tử. –

+0

Điều đó trong khi với 2 retries là một giải pháp rất bẩn, chờ đợi nên được ưa thích. Xem giải pháp của tôi – cocorossello

11

Nói chung điều này là do DOM được cập nhật và bạn cố gắng truy cập vào một/yếu tố mới được cập nhật - nhưng của sảng khoái vì vậy nó là một tham chiếu không hợp lệ bạn có ..

DOM

Làm quen với điều này bằng cách sử dụng phần tử chờ đợi rõ ràng trên phần tử để đảm bảo cập nhật hoàn tất, sau đó lấy lại tham chiếu mới cho phần tử.

Dưới đây là một số mã giả để minh họa (Phỏng theo một số mã C# tôi sử dụng cho CHÍNH XÁC vấn đề này):

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. 

Hope this helps!

40

Tôi gặp sự cố này không liên tục. Không biết với tôi, BackboneJS đang chạy trên trang và thay thế phần tử mà tôi đang cố gắng nhấp vào. Mã của tôi trông như thế này.

driver.findElement(By.id("checkoutLink")).click(); 

Tất nhiên là có chức năng giống như thế này.

WebElement checkoutLink = driver.findElement(By.id("checkoutLink")); 
checkoutLink.click(); 

Điều gì sẽ xảy ra thường xuyên là javascript sẽ thay thế phần tử checkoutLink giữa tìm và nhấp vào nó, tức là.

WebElement checkoutLink = driver.findElement(By.id("checkoutLink")); 
// javascript replaces checkoutLink 
checkoutLink.click(); 

Điều gì đã dẫn đến StaleElementReferenceException đúng khi cố gắng nhấp vào liên kết. Tôi không thể tìm thấy bất kỳ cách đáng tin cậy để nói với WebDriver để chờ cho đến khi javascript đã chạy xong, vì vậy đây là cách cuối cùng tôi đã giải quyết nó.

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; 
     } 
    }); 

Mã này sẽ liên tục cố nhấp vào liên kết, bỏ qua StaleElementReferenceChú ý cho đến khi nhấp chuột thành công hoặc hết thời gian chờ. Tôi thích giải pháp này vì nó tiết kiệm cho bạn phải viết bất kỳ logic thử lại nào và chỉ sử dụng các cấu trúc dựng sẵn của WebDriver.

-4

Có thể nó đã được thêm gần đây, nhưng các câu trả lời khác không đề cập đến tính năng chờ ngầm của Selenium, tất cả những điều trên đều dành cho bạn và được tích hợp vào Selenium.

driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);

này sẽ thử lại findElement() cuộc gọi cho đến khi nguyên tố này đã được tìm thấy, hoặc trong 10 giây.

Nguồn - http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp

+1

Giải pháp đó không ngăn StaleElementReferenceException – MrSpock

+0

Chỉ để loại bỏ bất kỳ sự nhầm lẫn nào trên các phiên bản, ngay cả trong phiên bản mới nhất của Selenium implicitlyWait() KHÔNG ngăn chặn StaleElementReferenceException. Tôi sử dụng một phương thức gọi trong vòng lặp với giấc ngủ, cho đến khi thành công hoặc số cố định. –

1

Một giải pháp trong C# sẽ là:

Helper lớp:

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; 
     } 
    } 
} 

Invocation:

 DriverHelper driverHelper = new DriverHelper("http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp", 10); 
     driverHelper.ClickElement("input[value='csharp']:first-child"); 

Tương tự như vậy có thể được sử dụng cho Java.

0

Lý do tại sao xảy ra StaleElementReferenceException đã được đưa ra: cập nhật cho DOM giữa việc tìm kiếm và thực hiện điều gì đó với phần tử.

Đối với nhấp Vấn đề tôi đã sử dụng gần đây một giải pháp như thế này:

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(); 
} 

Các phần quan trọng là "chain" của của Selenium riêng ExpectedConditions qua ExpectedConditions.refreshed(). Điều này thực sự chờ đợi và kiểm tra xem phần tử được đề cập đã được làm mới trong thời gian chờ đã chỉ định hay chưa và chờ đợi cho phần tử trở thành có thể nhấp.

Hãy xem qua số documentation for the refreshed method.

4

giải pháp Kenny là tốt, tuy nhiên nó có thể được viết theo một cách thanh lịch hơn

new WebDriverWait(driver, timeout) 
     .ignoring(StaleElementReferenceException.class) 
     .until((WebDriver d) -> { 
      d.findElement(By.id("checkoutLink")).click(); 
      return true; 
     }); 

Hoặc thêm:

new WebDriverWait(driver, timeout).ignoring(StaleElementReferenceException.class).until(ExpectedConditions.elementToBeClickable(By.id("checkoutLink"))); 
driver.findElement(By.id("checkoutLink")).click(); 
0

Trong dự án của tôi, tôi đã giới thiệu một khái niệm StableWebElement. Nó là một wrapper cho WebElement mà có thể phát hiện nếu phần tử là cũ và tìm một tham chiếu mới cho phần tử gốc. Tôi đã thêm một phương thức trợ giúp để định vị các phần tử trả về StableWebElement thay vì WebElement và vấn đề với StaleElementReference đã biến mất.

public static IStableWebElement FindStableElement(this ISearchContext context, By by) 
{ 
    var element = context.FindElement(by); 
    return new StableWebElement(context, element, by, SearchApproachType.First); 
} 

Mã trong C# có sẵn trên trang của dự án của tôi, nhưng nó có thể dễ dàng chuyển sang java https://github.com/cezarypiatek/Tellurium/blob/master/Src/MvcPages/SeleniumUtils/StableWebElement.cs

0

này làm việc cho tôi (100% lao động) sử dụng C#

public Boolean RetryingFindClick(IWebElement webElement) 
    { 
     Boolean result = false; 
     int attempts = 0; 
     while (attempts < 2) 
     { 
      try 
      { 
       webElement.Click(); 
       result = true; 
       break; 
      } 
      catch (StaleElementReferenceException e) 
      { 
       Logging.Text(e.Message); 
      } 
      attempts++; 
     } 
     return result; 
    } 
Các vấn đề liên quan