Wdrażam wiele testów Selenium przy użyciu Javy. Czasami moje testy kończą się niepowodzeniem z powodu StaleElementReferenceException
. Czy mógłbyś zaproponować kilka podejść do stabilizacji testów?Jak uniknąć "StaleElementReferenceException" w Selenium?
Odpowiedz
Może się tak zdarzyć, jeśli operacja DOM na stronie tymczasowo powoduje, że element nie jest dostępny. Aby umożliwić te przypadki, możesz spróbować uzyskać dostęp do elementu kilka razy w pętli, zanim w końcu rzucisz wyjątek.
Spróbuj 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;
}
Zazwyczaj jest to spowodowane DOM aktualizowany i próbuje uzyskać dostęp do zaktualizowanej/nowy element - ale Doma odświeżony więc nieprawidłowy odniesienie masz ..
Najpierw obejmij to, używając wyraźnego oczekiwania na element, aby upewnić się, że aktualizacja została zakończona, a następnie ponownie pobierz nowe odniesienie do elementu.
Oto niektóre kodu do zilustrowania psuedo (Zaczerpnięte z niektórych kodu C# używam do dokładnie tym numerze):
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.
Nadzieja to pomaga!
Miałem ten problem sporadycznie. Bez wiedzy, BackboneJS działał na stronie i zastępował element, który próbowałem kliknąć. Mój kod wyglądał tak.
driver.findElement(By.id("checkoutLink")).click();
Który jest oczywiście funkcjonalnie taki sam jak ten.
WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
checkoutLink.click();
Co by się stało, to javascript zastąpiłby element checkoutLink pomiędzy znajdowaniem i klikaniem, czyli.
WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
// javascript replaces checkoutLink
checkoutLink.click();
Które słusznie doprowadziła do StaleElementReferenceException podczas próby kliknij link. Nie mogłem znaleźć żadnego wiarygodnego sposobu, aby poinformować WebDrivera, że powinien poczekać, aż javascript się skończy, więc oto jak ostatecznie go rozwiązałem.
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;
}
});
Kod ten będzie stale próbować kliknij link, ignorując StaleElementReferenceExceptions dopóki nie uda kliknięcia lub limit czasu zostanie osiągnięty. Podoba mi się to rozwiązanie, ponieważ pozwala zaoszczędzić na pisaniu jakiejkolwiek logiki ponownej próby i wykorzystuje tylko wbudowane konstrukcje WebDrivera.
Być może dodano go niedawno, ale inne odpowiedzi nie wspominają o domyślnej funkcji czekania Selenium, która spełnia wszystkie powyższe warunki i jest wbudowana w Selen.
driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);
To będzie ponowić findElement()
połączeń aż element został znaleziony, lub przez 10 sekund.
Source - http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp
To rozwiązanie nie zapobiega StaleElementReferenceException – MrSpock
Tylko w celu wyeliminowania wszelkich nieporozumień w wersjach, nawet w najnowszej wersji programu implicitlyWait() NIE zapobiega wystąpieniu wyjątku StaleElementReferenceException. Używam metody, która wywołuje pętlę ze snu, aż do sukcesu lub ustalonej liczby. –
Rozwiązanie w C# byłoby:
klasa Pomocnik:
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;
}
}
}
Inwokacja:
DriverHelper driverHelper = new DriverHelper("http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp", 10);
driverHelper.ClickElement("input[value='csharp']:first-child");
Podobnie może być stosowany do Javy.
Powód, dla którego występuje StaleElementReferenceException
, został już opracowany: aktualizacje DOM między znalezieniem a zrobieniem czegoś z elementem.
Dla click-Problem I niedawno używany rozwiązanie takiego:
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();
}
Zasadniczą częścią jest „łańcuchowym” selenu własnego ExpectedConditions
poprzez ExpectedConditions.refreshed()
. To faktycznie czeka i sprawdza, czy element został odświeżony podczas określonego czasu oczekiwania i dodatkowo czeka, aż element stanie się klikalny.
Spójrz na documentation for the refreshed method.
Kenny rozwiązanie jest dobre, jednak to może być napisany w bardziej elegancki sposób
new WebDriverWait(driver, timeout)
.ignoring(StaleElementReferenceException.class)
.until((WebDriver d) -> {
d.findElement(By.id("checkoutLink")).click();
return true;
});
Albo też:
new WebDriverWait(driver, timeout).ignoring(StaleElementReferenceException.class).until(ExpectedConditions.elementToBeClickable(By.id("checkoutLink")));
driver.findElement(By.id("checkoutLink")).click();
W moim projekcie wprowadził pojęcie StableWebElement. Jest to wrapper dla WebElement, który jest w stanie wykryć, czy element jest Stale i znaleźć nowe odniesienie do oryginalnego elementu. Dodałem metody pomocnika do lokalizowania elementów, które zwracają StableWebElement zamiast WebElement i problem z StaleElementReference zniknął.
public static IStableWebElement FindStableElement(this ISearchContext context, By by)
{
var element = context.FindElement(by);
return new StableWebElement(context, element, by, SearchApproachType.First);
}
Kod w C# jest dostępny na stronie mojego projektu, ale to może być łatwo przeniesione do java https://github.com/cezarypiatek/Tellurium/blob/master/Src/MvcPages/SeleniumUtils/StableWebElement.cs
Działa to dla mnie (100% sprawny) przy użyciu 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;
}
- 1. Jak mogę uniknąć apostrofu w moim zapytaniu tekstowym XPath z Perl i Selenium?
- 2. Jak uruchomić Firebug w Selenium WebDriver (Selenium 2)?
- 3. Jak uniknąć nawiasów w grep
- 4. Jak uniknąć duplikowania w GROUP_CONCAT?
- 5. Jak uniknąć "i" w XML?
- 6. Jak uniknąć buforowania w sqlalchemy?
- 7. Jak uniknąć zaokrąglania w NSNumberFormatter
- 8. Selenium WebDriver - definiuje selektory WebElement jako stały dobry pomysł?
- 9. Jak uniknąć downcast?
- 10. Używanie Selenium w tle
- 11. Jak uniknąć brakuPropertyException
- 12. Existential antipattern, jak uniknąć
- 13. Bash: Jak uniknąć $ @?
- 14. Jak uniknąć NHibernate.NonUniqueObjectException
- 15. jak uniknąć skojarzeń polimorficznych
- 16. Jak uniknąć konstruktorów kodów?
- 17. Jak uniknąć etykiet wejść
- 18. Jak zainstalować Selenium IDE w Mozilla Firefox
- 19. Jak nagrać wideo w Selenium webdriver
- 20. Selenium-rc: Jak używać CaptureNetworkTraffic w pytonie
- 21. Jak uzyskać macierzysty rejestrator w Selenium WebDriver
- 22. Jak uzyskać nagłówki odpowiedzi w Selenium?
- 23. Jak czekać na przekierowanie strony w Selenium?
- 24. Jak zapętlić testy w Selenium IDE?
- 25. Jak wykonać testy Selenium 2 w Jenkinsach
- 26. Jak uniknąć zagnieżdżonych ciężarów?
- 27. Jak uniknąć System.IO.PathTooLongException?
- 28. Jak uniknąć flashowania WPF
- 29. Sterownik Selenium: jak przetestować rozmycie?
- 30. Selenium waitForCondition
Wow! Tego właśnie potrzebowałem. Dzięki! – SpartaSixZero
Można to również naprawić, używając innego odniesienia elementu. –
To, że przy 2 próbach jest bardzo brudnym rozwiązaniem, powinno być preferowane oczekiwanie. Zobacz moje rozwiązanie – cocorossello