t.me/atinfo_chat Telegram группа по автоматизации тестирования

Расширение и изменение функционала фреймворков

Теги: #<Tag:0x00007fd77e2040d8> #<Tag:0x00007fd77e213ce0> #<Tag:0x00007fd77e213b50> #<Tag:0x00007fd77e213a10> #<Tag:0x00007fd77e213718>

Здравствуйте. Вопрос скорее не по автоматизации,а общих принципах и умениях программировать.
Но так как мой вопрос в контексте Selenide, решил задать его здесь.

Как можно расширить/изменить функционал какого нибудь метода из фреймворка (здесь Selenide), так, чтобы основной код использовался из самой библиотеки, а видоизменённый метод - из моего класса?

Я читал про наследование и переопределение методов, но не могу применить прочтённые знания к такому вопросу.

Дело не в том, что мне что то не нравится или не хватает в Selenide, дело лишь в желании понять, как это можно сделать, чтобы применять это так же в других случаях.

Очень странно, но в Селелениде есть всё что нужно для автоматизации, странно что вам чего-то не хватает.
Вот пример недавний, как писать свои методы, хотя это несколько не то о чём вы писали: Вопрос по Selenide + Java, пытаюсь сократить код.
Лучше уж приводите более конкретный пример.

Да мне в принципе всё хватает. Я же написал про это в своём первом посте.
Я просто не знаю с какой стороны подойти, чтобы привести пример.

Если скажу - а давайте попробуем переопределить вот этот метод, вы же скажите - а зачем? он и так работает? да, работает там всё…не спорю.

Мне просто интересно как это можно сделать так, чтобы основной фреймворк работал как и прежде, а отдельно взятый метод был переопределён.

Пусть это будет вот этот метод:

public class Selenide {

 @CheckReturnValue
  public static SelenideElement $(By seleniumSelector) {
    return getSelenideDriver().find(seleniumSelector);
  }

пусть внутри я добавил

 @CheckReturnValue
  public static SelenideElement $(By seleniumSelector) {
   print("searching element...");
   return getSelenideDriver().find(seleniumSelector);
  }

Как теперь сделать так, чтобы это заработало? Или это невозможно.? Или это вообще так никто не делает ?
Просто читая различную документацию по наследованию, переопределению методов видишь либо самые элементарные примеры, которые понятны, либо 800 страниц теории с рассказами о все возможных абстракциях, и таким авторам тоже хочется сказать - Вы бы лучше нормальные примеры из своих проектов показали… вы же где то опыт то набрали.

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

  1. Создаём доп. класс, для того то
  2. в нём определяем то то, это будет служить как связь с тем то…
    или что то типо того

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

Нуууу, надо так надо. Создаёте класс, в нём делаете уже свой метод, который делает то что вам нужно, и используете уже этот метод, например:

  public static SelenideElement $(By seleniumSelector) {
      print("searching element...");
      return Selenide.$(seleniumSelector);
  }
1 Симпатия

Начать нужно с того, что для статических методов/полей переопределение (overriding) не применимо в принципе. Для статических методов используется hiding, и это совсем другое, поэтому пример с public static SelenideElement $ не подходит.

Во-вторых, авторы фреймворка могут предусмотреть возможность переопределения/расширения какой-то функциональности, предоставляя пользователю интерфейсы и способы регистрации кастомной имплементации интерфейсов в фреймворке. Например, Extensions в JUnit 5, Listeners в TestNG, Object Factory в кукумбере. Но этот вариант даёт возможность переопределить только ограниченный набор методов/классов, которые автор фреймворка разрешил переопределить.

В-третьих, есть библиотеки для модифицикации байткода в рантайме, с помощью которых можно создавать dynamic proxy для любого (почти) класса и оверрайдить методы как угодно. Или перехватывать вызовы методов и выполнять некий код. Таким образом, например, реализованны мок-объекты в mockito или репортинг в аллюре. Примеры библиотек - ByteBuddy, cglib, AspectJ. Но этот способ переопределения нужно использовать только когда точно понимаешь, что и зачем ты делаешь, то есть практически никогда.

В-четвертых, есть дизайн паттерны (facade, adapter), с помощью которых можно писать враперы для сторонних библиотек в своём коде и в тестах вызывать враперы, а не методы фреймворка.

В-пятых, всегда можно сделать форк библиотеки и написать нужные фичи прямо в нём.

Если говорить о конкретном примере с добавлением логгирования

 @CheckReturnValue
  public static SelenideElement $(By seleniumSelector) {
   print("searching element...");
   return getSelenideDriver().find(seleniumSelector);
  }

то для такого можно использовать AspectJ, например: https://www.yegor256.com/2014/06/01/aop-aspectj-java-method-logging.html

1 Симпатия

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

Конкретно в селениде можно переопределить любые Command и Condition.

Например, так:

Commands.getInstance().add("click", new MyClick());

См. https://github.com/selenide/selenide/blob/master/src/test/java/integration/OverrideCommandsTest.java

RTFT!

2 Симпатий