Как получить объект WebDriver из объекта WebElement в автоматизированных тестах на Selenium в Java

Не смотря на то, что публичного интерфейса для получения экземпляра WebDriver из экземпляра WebElement не существует, иногда в автотестах на Selenium такую возможность иметь всё же требуется. С некоторыми допущениями, такая возможность существует. Мы посмотрим на один из таких примеров.

Описание примера: имея WebElement получаем WebDriver

Используя стандартный способ поиска элементов на странице, мы получим некоторый WebElement и затем извлечём из него WebDriver, использованный для поиска этого элемента. Мы воспользуемся механизмом рефлексии для внедрения в реализацию элемента где изменим модификатор доступа к полю объекта.

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

Почему такой подход не всегда возможен

Говоря просто, WebDriver и WebElement - это всего лишь интерфейсы. Они определяют контракт, которому должны следовать реализации этих интерфейсов. Контракт WebElement просто не предполагает возможности извлечения экземпляра драйвера из себя.

В то же время Selenium предоставляет ряд реализаций упомянутых интерфейсов, такие как, например RemoteWebDriver и RemoteWebElement. Последний, в частности, хранит ссылку на родительский драйвер. Хорошей новостью здесь является то, что большинство распространенных драйверов (например FirefoxDriver или ChromeDriver) являются наследниками RemoteWebDriver. Есть и плохая новость: поле, которое нам необходимо имеет модификатор доступа protected.

Итак, почему способ будет работать не всегда? Потому что ваш драйвер по неудачному стечению обстоятельств может и не являться наследником упомянутого класса.

Реализация подхода

Ниже показан пример метода, возвращающего объект WebDriver для заданного экземпляра WebElement. Так как мы не можем просто обратиться к нужному полю (оно помечено как protected), нам необходимо прибегнуть к рефлексии, для обхода этого ограничения.

private WebDriver getWebDriverFromWebElement(WebElement webElement){
    if(!webElement.getClass().isAssignableFrom(RemoteWebElement.class)){
        return null;
    }
    try {
        Field parent = webElement.getClass().getDeclaredField("parent");
        parent.setAccessible(true);
        return (RemoteWebDriver)parent.get(webElement);
    } catch (NoSuchFieldException | IllegalAccessException e) {
        return null;
    }
}

Если при попытке обращения к полю возникает какая-то проблема, метод возвращает пустую ссылку. Метод протестирован для следующих драйверов: FirefoxDriver и ChromeDriver.

Тестируем реализацию

Давайте напишем простой тест, проверяющий работоспособность нашего метода:

@Test
public void testWebDriver(){
    driver.get("https://webelement.click/en/welcome");
    WebElement element = driver.findElement(By.tagName("li"));
    System.out.println(driver.equals(getWebDriverFromWebElement(element)));
}

Результат теста показывает идентичность объектов.

Если у вас остались вопросы, задавайте их тут. Я постараюсь дополнить статью опираясь на ваши замечания.