Не смотря на то, что публичного интерфейса для получения экземпляра 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))); }
Результат теста показывает идентичность объектов.
Если у вас остались вопросы, задавайте их тут. Я постараюсь дополнить статью опираясь на ваши замечания.