it-swarm.com.ru

Как решить, исключение устаревшего элемента? если элемент больше не привязан к DOM?

У меня есть вопрос относительно "Элемент больше не привязан к DOM".

Я пробовал разные решения, но они работают с перебоями. Пожалуйста, предложите решение, которое может быть постоянным.

WebElement getStaleElemById(String id, WebDriver driver) {
    try {
        return driver.findElement(By.id(id));
    } catch (StaleElementReferenceException e) {
        System.out.println("Attempting to recover from StaleElementReferenceException ...");
        return getStaleElemById(id, driver);
    }
}

WebElement getStaleElemByCss(String css, WebDriver driver) {
    try {
        return driver.findElement(By.cssSelector(css));
    } catch (StaleElementReferenceException e) {
        System.out.println("Attempting to recover from StaleElementReferenceException ...");
        return getStaleElemByCss(css, driver);
    } catch (NoSuchElementException ele) {
         System.out.println("Attempting to recover from NoSuchElementException ...");
         return getStaleElemByCss(css, driver);
    }
}

Спасибо, Ана

14
user2498024

Эта проблема

Проблема, с которой вы, вероятно, сталкиваетесь, заключается в том, что метод возвращает правильный (и действительный!) Элемент, но когда вы пытаетесь получить к нему доступ через секунду, он устарел и выбрасывает.

Это обычно возникает, когда:

  1. Вы щелкаете что-то, что загружает новую страницу асинхронно или, по крайней мере, изменяет ее.
  2. Вы немедленно (до окончания загрузки страницы) ищете элемент ... и вы нашли его!
  3. Страница, наконец, выгружается, а новая загружается.
  4. Вы пытаетесь получить доступ к ранее найденному элементу, но теперь он устарел, хотя новая страница также содержит его.

Решения

Есть четыре способа решения этой проблемы, о которых я знаю:

  1. Используйте правильные ожидания

    Используйте правильные ожидания после каждой ожидаемой загрузки страницы при обращении к асинхронным страницам. Вставьте явное ожидание после первого щелчка и дождитесь загрузки новой страницы/нового контента. Только после этого вы можете попытаться найти нужный элемент. Это должно быть первым, что вы сделаете. Это значительно повысит надежность ваших тестов.

  2. То, как вы это сделали

    Я уже два года использую вариант вашего метода (вместе с методикой, описанной выше в решении 1), и он абсолютно работает большую часть времени и дает сбой только при странных ошибках WebDriver. Попробуйте получить доступ к найденному элементу сразу после того, как он найден (перед возвратом из метода) с помощью метода .isDisplayed() или чего-то еще. Если он выбрасывает, вы уже знаете, как искать снова. Если это пройдет, у вас есть еще одна (ложная) уверенность.

  3. Используйте WebElement, который снова находит себя, когда устарел

    Напишите WebElement декоратор, который запоминает, как он был найден, и повторно находит его, когда к нему обращаются и выдает. Это, очевидно, вынуждает вас использовать собственные методы findElement(), которые будут возвращать экземпляры вашего декоратора (или, что еще лучше, украшенные WebDriver, которые будут возвращать ваши экземпляры из обычных методов findElement() и findElemens()). Сделай это так:

    public class NeverStaleWebElement implements WebElement {
        private WebElement element;
        private final WebDriver driver;
        private final By foundBy;
    
        public NeverStaleWebElement(WebElement element, WebDriver driver, By foundBy) {
            this.element = element;
            this.driver = driver;
            this.foundBy = foundBy;
        }
    
        @Override
        public void click() {
            try {
                element.click();
            } catch (StaleElementReferenceException e) {
                // log exception
    
                // assumes implicit wait, use custom findElement() methods for custom behaviour
                element = driver.findElement(foundBy);
    
                // recursion, consider a conditioned loop instead
                click();
            }
        }
    
        // ... similar for other methods, too
    
    }
    

    Обратите внимание, что, хотя я думаю, что информация foundBy должна быть доступна из общих WebElements, чтобы упростить эту задачу, разработчики Selenium считают ошибкой пробовать что-то подобное и решили не делать эту информацию общедоступной . Возможно, плохая практика повторного поиска устаревших элементов, потому что вы заново обнаруживаете элементы без какого-либо механизма проверки его оправданности. Механизм повторного поиска может потенциально найти совершенно другой элемент, а не тот же самый снова. Кроме того, он ужасно завершается с findElements(), когда есть много найденных элементов (вам нужно либо запретить повторный поиск элементов, найденных с помощью findElements(), либо вспомнить, сколько элементов ваш был из возвращенной List).

    Я думаю, что иногда это было бы полезно, но это правда, что никто никогда не использовал бы варианты 1 и 2, которые, очевидно, являются гораздо лучшими решениями для надежности ваших тестов. Используйте их и только после того, как вы убедитесь, что вам это нужно, пойти на это.

  4. Использовать очередь задач (которая может перезапускать прошлые задачи)

    Реализуйте весь свой рабочий процесс по-новому!

    • Создайте центральную очередь заданий для запуска. Сделайте эту очередь запоминающей прошлые работы.
    • Реализуйте каждую необходимую задачу («найдите элемент и щелкните по нему», «найдите элемент и отправьте ему ключи» и т.д.) С помощью шаблона Command. При вызове добавьте задачу в центральную очередь, которая затем (синхронно или асинхронно, не имеет значения) запустит ее.
    • При необходимости аннотируйте каждую задачу с помощью @LoadsNewPage, @Reversible и т.д.
    • Большинство ваших задач будут обрабатывать свои исключения самостоятельно, они должны быть автономными.
    • Когда очередь встречает исключение устаревшего элемента, она берет последнюю задачу из истории задач и запускает ее снова, чтобы повторить попытку.

    Это, очевидно, потребует много усилий и, если не будет хорошо продуман, может скоро привести к обратному результату. Я использовал (намного более сложный и мощный) вариант этого для возобновления неудачных тестов после того, как я вручную исправил страницу, на которой они находились. При некоторых условиях (например, в StaleElementException) сбой не сразу завершит тест, но подождет (до истечения тайм-аута через 15 секунд), отобразит информационное окно и предоставит пользователю возможность обновить вручную. страница/нажмите правую кнопку/исправить форму/что угодно. Затем он повторно запустит невыполненную задачу или даже даст возможность вернуться на несколько шагов назад в историю (например, к последнему заданию @LoadsNewPage).


Конечные кирки

Все это говорит, что ваше оригинальное решение может использовать некоторую полировку. Вы можете объединить два метода в один, более общий (или, по крайней мере, заставить их делегировать этот метод для уменьшения повторения кода):

WebElement getStaleElem(By by, WebDriver driver) {
    try {
        return driver.findElement(by);
    } catch (StaleElementReferenceException e) {
        System.out.println("Attempting to recover from StaleElementReferenceException ...");
        return getStaleElem(by, driver);
    } catch (NoSuchElementException ele) {
        System.out.println("Attempting to recover from NoSuchElementException ...");
        return getStaleElem(by, driver);
    }
}

В Java 7 достаточно даже одного многоадресного блока:

WebElement getStaleElem(By by, WebDriver driver) {
    try {
        return driver.findElement(by);
    } catch (StaleElementReferenceException | NoSuchElementException e) {
        System.out.println("Attempting to recover from " + e.getClass().getSimpleName() + "...");
        return getStaleElem(by, driver);
    }
}

Таким образом, вы можете значительно сократить объем кода, который необходимо поддерживать.

41
Petr Janeček

Я решаю эту проблему, сохраняя устаревший элемент и опрашивая его до тех пор, пока он не сгенерирует исключение, а затем ожидаю, пока элемент снова не станет видимым.

    boolean isStillOnOldPage = true;
    while (isStillOnOldPage) {
        try {
            theElement.getAttribute("whatever");
        } catch (StaleElementReferenceException e) {
            isStillOnOldPage = false;
        }
    }
    WebDriverWait wait = new WebDriverWait(driver, 15);
    wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("theElementId")));
1
Felix Dobslaw

Решения для их решения:

  1. Хранение локаторов для ваших элементов вместо ссылок
driver = webdriver.Firefox();
driver.get("http://www.github.com");
search_input = lambda: driver.find_element_by_name('q');
search_input().send_keys('hello world\n'); 
time.sleep(5);


search_input().send_keys('hello frank\n') // no stale element exception
  1. Используйте хуки в используемых библиотеках JS
   # Using Jquery queue to get animation queue length.
    animationQueueIs = """
    return $.queue( $("#%s")[0], "fx").length;
    """ % element_id
    wait_until(lambda: self.driver.execute_script(animationQueueIs)==0)
  1. Перемещение ваших действий в JavaScript-инъекцию
 self.driver.execute_script("$(\"li:contains('Narendra')\").click()");
  1. Предварительно дождаться устаревания элемента
  # Wait till the element goes stale, this means the list has updated
  wait_until(lambda: is_element_stale(old_link_reference))

Это решение, которое сработало для меня

0
NarendraC

Если вы пытаетесь нажать на ссылку, вы переходите на новую страницу. После этого вернитесь назад и нажмите на другие ссылки. Они ниже кода могут помочь вам.

public int getNumberOfElementsFound(By by) {
    return  driver.findElements(by).size();
  }

public WebElement getElementWithIndex(By by, int pos) {
    return driver.findElements(by).get(pos);
  }

/**click on each link */
public void getLinks()throws Exception{
try {
List<WebElement> componentList = driver.findElements(By.tagName("a"));
System.out.println(componentList.size()); 

    for (WebElement component : componentList)
    {
        //click1();
        System.out.println(component.getAttribute("href"));
    }
 int numberOfElementsFound = getNumberOfElementsFound(By.tagName("a"));
for (int pos = 0; pos < numberOfElementsFound; pos++) {
     if (getElementWithIndex(By.tagName("a"), pos).isDisplayed()){

  getElementWithIndex(By.tagName("a"), pos).click();
  Thread.sleep(200);
  driver.navigate().back();
  Thread.sleep(200);                                                       
}
  }
    }catch (Exception e){
        System.out.println("error in getLinks "+e);
    }
}
0
Shammi

Для Fitnesse вы можете использовать:

| начать | Smart Web Driver | Selenium.properties |

@Fixture (name = "Smart Web Driver") Открытый класс SmartWebDriver расширяет SlimWebDriver {

private final static Logger LOG = LoggerFactory.getLogger(SmartWebDriver.class);

/**
 * Constructs a new SmartWebDriver.
 */
@Start(name = "Start Smart Web Driver", arguments = {"configuration"}, example = "|start |Smart Web Driver| Selenium.properties|")
public SmartWebDriver(String configuration) {
    super(configuration);
}

/**
 * Waits for an element to become invisible (meaning visible and width and height != 0).
 *
 * @param locator the locator to use to find the element.
 */
@Command(name = "smartWaitForNotVisible", arguments = {"locator"}, example = "|smartWaitForNotVisible; |//path/to/input (of css=, id=, name=, classname=, link=, partiallink=)|")
public boolean smartWaitForNotVisible(String locator) {
    try {
        waitForNotVisible(locator);
    } catch (StaleElementReferenceException sere) {
        LOG.info("Element with locator '%s' did not become invisible (visible but also width and height != 0), a StaleElementReferenceException occurred, trying to continue...", locator);
    } catch (NoSuchElementException ele) {
        LOG.info("Element with locator '%s' did not become invisible (visible but also width and height != 0), a NoSuchElementException occurred, trying to continue...", locator);
    } catch (AssertionError ae) {
        if (ae.getMessage().contains("No element found")) {
            LOG.info("Element with locator '%s' did not become invisible (visible but also width and height != 0), a AssertionError occurred, trying to continue...", locator);
        } else {
            throw ae;
        }
    }
    return true;
}

}

0
Koenraad Appelo

Когда происходит исключение устаревшего элемента !!

Исключение устаревших элементов может произойти, когда библиотеки, поддерживающие эти текстовые поля/кнопки/ссылки, изменились, что означает, что элементы одинаковы, но теперь ссылка на сайте изменилась, не влияя на локаторы. Таким образом, ссылка, которую мы сохранили в нашем кэше, включая ссылку на библиотеку, теперь устарела или устарела, потому что страница была обновлена ​​обновленными библиотеками.

for(int j=0; j<5;j++)
try {
    WebElement elementName=driver.findElement(By.xpath(“somexpath”));
    break;
} catch(StaleElementReferenceException e){
e.toString();
System.out.println(“Stale element error, trying ::  ” + e.getMessage());
}
elementName.sendKeys(“xyz”);
0
Nag Raj