Java를 사용하여 많은 Selenium 테스트를 구현하고 있습니다. 때때로 내 테스트는 StaleElementReferenceException
. 테스트를보다 안정적으로 만드는 방법을 제안 해 주시겠습니까?
Java를 사용하여 많은 Selenium 테스트를 구현하고 있습니다. 때때로 내 테스트는 StaleElementReferenceException
. 테스트를보다 안정적으로 만드는 방법을 제안 해 주시겠습니까?
답변:
페이지에서 발생하는 DOM 작업으로 인해 일시적으로 요소에 액세스 할 수없는 경우 이러한 상황이 발생할 수 있습니다. 이러한 경우를 허용하기 위해 최종적으로 예외를 발생시키기 전에 루프에서 요소에 여러 번 액세스 할 수 있습니다.
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;
}
이 문제가 간헐적으로 발생했습니다. 나에게 알려지지 않은 BackboneJS가 페이지에서 실행 중이며 클릭하려는 요소를 대체했습니다. 내 코드는 다음과 같습니다.
driver.findElement(By.id("checkoutLink")).click();
물론 기능적으로 이것과 동일합니다.
WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
checkoutLink.click();
가끔 일어나는 일은 자바 스크립트가 그것을 찾아 클릭하는 사이에 checkoutLink 요소를 대체하는 것입니다.
WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
// javascript replaces checkoutLink
checkoutLink.click();
링크를 클릭하려고 할 때 StaleElementReferenceException이 정당하게 발생했습니다. WebDriver에게 자바 스크립트 실행이 끝날 때까지 기다리라고하는 신뢰할 수있는 방법을 찾을 수 없었기 때문에 결국 해결 방법은 다음과 같습니다.
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를 무시하고 링크를 계속 클릭하려고합니다. 이 솔루션을 사용하면 재시도 논리를 작성하지 않아도되고 WebDriver의 기본 제공 구조 만 사용하므로이 솔루션을 좋아합니다.
일반적으로 이는 DOM이 업데이트되고 업데이트 된 / 새 요소에 액세스하려고 시도하기 때문입니다.하지만 DOM이 새로 고쳐 져서 잘못된 참조입니다.
먼저 요소에 대한 명시 적 대기를 사용하여 업데이트가 완료되었는지 확인한 다음 요소에 대한 새로운 참조를 다시 가져와이 문제를 해결하십시오.
여기에 (I가 사용하는 몇 가지 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.
도움이 되었기를 바랍니다!
Kenny의 솔루션은 좋지만 더 우아한 방식으로 작성할 수 있습니다.
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();
그러나 어쨌든 최선의 해결책은 Selenide 라이브러리에 의존하는 것입니다.이 라이브러리는 이런 종류의 것들을 처리합니다. (요소 참조 대신 프록시를 처리하므로 오래된 요소를 처리 할 필요가 없습니다. 이는 매우 어려울 수 있습니다). 셀레 나이드
StaleElementReferenceException
발생 하는 이유 는 이미 설명되어 있습니다. 요소를 사용하여 무언가를 찾고 수행하는 사이의 DOM 업데이트입니다.
클릭 문제의 경우 최근 다음과 같은 솔루션을 사용했습니다.
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();
}
중요한 부분은 .NET Framework ExpectedConditions
를 통한 셀레늄 자체의 "체인"입니다 ExpectedConditions.refreshed()
. 이것은 실제로 해당 요소가 지정된 시간 제한 동안 새로 고쳐 졌는지 확인하고 추가로 요소를 클릭 할 수있을 때까지 기다립니다.
내 프로젝트에서 StableWebElement 개념을 도입했습니다. 요소가 Stale인지 감지하고 원래 요소에 대한 새 참조를 찾을 수있는 WebElement 용 래퍼입니다. WebElement 대신 StableWebElement를 반환하는 요소를 찾는 데 도우미 메서드를 추가했으며 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 로 쉽게 이식 할 수 있습니다.
C #의 솔루션은 다음과 같습니다.
도우미 클래스 :
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에도 사용할 수 있습니다.
Kenny의 솔루션은 더 이상 사용되지 않습니다. 이것을 사용하면 작업 클래스를 사용하여 두 번 클릭하지만 무엇이든 할 수 있습니다.
new FluentWait<>(driver).withTimeout(30, TimeUnit.SECONDS).pollingEvery(5, TimeUnit.SECONDS)
.ignoring(StaleElementReferenceException.class)
.until(new Function() {
@Override
public Object apply(Object arg0) {
WebElement e = driver.findelement(By.xpath(locatorKey));
Actions action = new Actions(driver);
action.moveToElement(e).doubleClick().perform();
return true;
}
});
findByAndroidId
우아의 처리하는 방법 StaleElementReference
.이것은 jspcal의 답변을 기반으로 하지만 우리 설정에서 깔끔하게 작동하도록 답변을 수정해야했기 때문에 다른 사람들에게 도움이되는 경우 여기에 추가하고 싶었습니다. 이 답변이 도움이 되었다면 jspcal의 답변을 올려 주세요 .
// This loops gracefully handles StateElementReference errors and retries up to 10 times. These can occur when an element, like a modal or notification, is no longer available.
export async function findByAndroidId( id, { assert = wd.asserters.isDisplayed, timeout = 10000, interval = 100 } = {} ) {
MAX_ATTEMPTS = 10;
let attempt = 0;
while( attempt < MAX_ATTEMPTS ) {
try {
return await this.waitForElementById( `android:id/${ id }`, assert, timeout, interval );
}
catch ( error ) {
if ( error.message.includes( "StaleElementReference" ) )
attempt++;
else
throw error; // Re-throws the error so the test fails as normal if the assertion fails.
}
}
}
이것은 C #을 사용하여 나를 위해 작동합니다 (100 % 작동).
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;
}
문제는 Javascript에서 Java로 요소를 다시 Javascript로 전달할 때 DOM을 떠날 수 있다는 것입니다.
Javascript에서 모든 것을 시도하십시오.
driver.executeScript("document.querySelector('#my_id').click()")
이 시도
while (true) { // loops forever until break
try { // checks code for exceptions
WebElement ele=
(WebElement)wait.until(ExpectedConditions.elementToBeClickable((By.xpath(Xpath))));
break; // if no exceptions breaks out of loop
}
catch (org.openqa.selenium.StaleElementReferenceException e1) {
Thread.sleep(3000); // you can set your value here maybe 2 secs
continue; // continues to loop if exception is found
}
}
여기 에서 해결책을 찾았 습니다 . 제 경우에는 현재 창, 탭 또는 페이지를 떠나 다시 돌아 오는 경우 요소에 액세스 할 수 없게됩니다.
.ignoring (StaleElement ...), .refreshed (...) 및 elementToBeClicable (...)은 도움이되지 않았고 act.doubleClick(element).build().perform();
문자열에 예외가 발생했습니다 .
내 주요 테스트 클래스에서 기능 사용 :
openForm(someXpath);
내 BaseTest 기능 :
int defaultTime = 15;
boolean openForm(String myXpath) throws Exception {
int count = 0;
boolean clicked = false;
while (count < 4 || !clicked) {
try {
WebElement element = getWebElClickable(myXpath,defaultTime);
act.doubleClick(element).build().perform();
clicked = true;
print("Element have been clicked!");
break;
} catch (StaleElementReferenceException sere) {
sere.toString();
print("Trying to recover from: "+sere.getMessage());
count=count+1;
}
}
내 BaseClass 함수 :
protected WebElement getWebElClickable(String xpath, int waitSeconds) {
wait = new WebDriverWait(driver, waitSeconds);
return wait.ignoring(StaleElementReferenceException.class).until(
ExpectedConditions.refreshed(ExpectedConditions.elementToBeClickable(By.xpath(xpath))));
}
(작업과 관련하여) 지금까지 아무도 언급하지 않은 StaleElementReferenceException으로 이어지는 잠재적 인 문제가있을 수 있습니다.
Javascript로 설명하지만 Java에서도 동일합니다.
작동하지 않습니다.
let actions = driver.actions({ bridge: true })
let a = await driver.findElement(By.css('#a'))
await actions.click(a).perform() // this leads to a DOM change, #b will be removed and added again to the DOM.
let b = await driver.findElement(By.css('#b'))
await actions.click(b).perform()
그러나 작업을 다시 인스턴스화하면 해결됩니다.
let actions = driver.actions({ bridge: true })
let a = await driver.findElement(By.css('#a'))
await actions.click(a).perform() // this leads to a DOM change, #b will be removed and added again to the DOM.
actions = driver.actions({ bridge: true }) // new
let b = await driver.findElement(By.css('#b'))
await actions.click(b).perform()
일반적으로 StaleElementReferenceException 우리가 접근하려는 요소가 나타 났지만 다른 요소가 우리가 포함 된 요소의 위치에 영향을 미칠 수 있으므로 클릭 또는 getText를 시도하거나 WebElement에서 일부 작업을 시도 할 때 일반적으로 DOM에 첨부되지 않은 요소를 나타내는 예외가 발생합니다. .
내가 시도한 해결책은 다음과 같습니다.
protected void clickOnElement(By by) {
try {
waitForElementToBeClickableBy(by).click();
} catch (StaleElementReferenceException e) {
for (int attempts = 1; attempts < 100; attempts++) {
try {
waitFor(500);
logger.info("Stale element found retrying:" + attempts);
waitForElementToBeClickableBy(by).click();
break;
} catch (StaleElementReferenceException e1) {
logger.info("Stale element found retrying:" + attempts);
}
}
}
protected WebElement waitForElementToBeClickableBy(By by) {
WebDriverWait wait = new WebDriverWait(getDriver(), 10);
return wait.until(ExpectedConditions.elementToBeClickable(by));
}
위의 코드에서 나는 먼저 기다렸다가 예외가 발생하면 요소를 클릭 한 다음, 여전히 모든 요소가로드되지 않고 다시 예외가 발생할 가능성이 있으므로이를 포착하고 루프를 시도합니다.
아마도 더 최근에 추가되었지만 다른 답변은 위의 모든 작업을 수행하고 Selenium에 내장 된 Selenium의 암시 적 대기 기능을 언급하지 않습니다.
driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);
findElement()
요소를 찾을 때까지 또는 10 초 동안 호출 을 재 시도 합니다.
출처-http: //www.seleniumhq.org/docs/04_webdriver_advanced.jsp