Оптимизация Framework или одинаковый функционал на некоторых страницах

(Java) У меня в админке имеется List2List:

Он может быть как на главной странице и на самой форме и на Popup окне. Все они одинаковы, различаются только локатором и значениями в нем, но функционал на всех листах похож. Также один и тот же List2List может встретиться на разных страницах, но его также может и не быть (кстати аналогичная ситуация с другими полями).

Посмотрев видео про PageObject pattern понял, что нужно описывать каждую страницу отдельно. А если на странице есть функционал более сложный выносить его в отдельный класс. Также если происходит дублирование кода, то нужно тоже задуматься о том стоит ли его вынести.

Я решил сделать так: есть класс Karaoke, в нем присутствует метод editList, который просто возвращает экземпляр класса List2List, в нем содержатся все значения элементов этих листов, которые могут встретиться в админке. Каждый из методов передает элемент в следующий класс EditList2List и уже в нем описана функциональность (удалить и добавить значение, добавить все и удалить все):

public class Karaoke extends Page{

...
public List2List editList(){
return new List2List(driver)
}
...
}

public class List2List{

	@FindBy(css = "section[name='row.__states.Genres']")
	 public WebElement states_Genres;
...
public Genres(){
return new EditList2List(states_Genres);
}
...
}

public class EditList2List{
...
WebElement element;
	public List2List(WebElement element) {
		this.element = element;
	}

	public void addAll(){
		element.findElement(By.cssSelector("[ng-click='addAll()']")).click();
	}
		
	public void deleteAll(){
		element.findElement(By.cssSelector("[ng-click='deleteAll()']")).click();
	}
	
	public void addValue(){
		
	}
	
	public void deleteValue(){
		
	}
...
}

По сути это поможет мне писать в тесте:

karaoke.editList2List().genres().addAll();

Такой способ верный? Также интересует в общем что делать с более сложным функционалом на странице, который может повторятся, а может нет.

Более-менее нормально. Мне лично только не нравится класс EditList2List. Классы - это обычно существительные, а глаголы - это методы классов. Я бы все методы работы со списком делал просто в классе List2List, класс EditList2List тут лишний на мой взгляд

Мне тоже так показалось. спасибо. А по поводу второго вопроса, также поступать как с Листами?

Да, повторяющийся функционал принято выносить в отдельные элементы (методы, классы и т.д.), которые:

  1. инкапсулируют в себе необходимую логику поведения элемента
  2. могут быть переиспользованы

А если например представить, что (элементов стало много) есть в общей сложности 50 видов кнопок и 1000. И они могут быть на странице, а могут не быть, ну и различные комбинации бывают. В этом случае я думал что нужно делать некую видимость элементов для конкретной страницы (karaoke.footer().save(); // после футера видно будет только 15 методов) или все же нужно в один класс все 50 кнопок поместить и по мере необходимости унаследовать этот класс?

P.S. вопрос практически похожий но я хотел это посмотреть с более глобальной точки зрения.

  1. Любой повторяющийся элемент интерфейса - это отдельный класс, реализующий паттерн PageObject
  2. Сложность возникает тогда, когда один и тот же элемент интерфейса, например, список объектов, который с виду выглядит одинаково на разных страницах и так и просится выделить его в отдельный класс и переиспользовать, в реальности оказывается реализован на разных страницах по-разному: имеет разную верстку, разные id элементов и т.д.

Придерживайся правила в п.1 и не забывай по поводу п.2 требовать с разработчиков одинаковой реализации одинаковых элементов на разных страницах

Хорошо, но если я вынес элементы в отдельный класс (Button.class) и тут встречается страница где 3 кнопки отсутствуют. Я наследую класс с элементами кнопок для Karaoke.class, но в этом случае будет видимость нескольких кнопок которые не доступны по сути у Karaoke.class такое поведение правильно?

karaoke.footer().save(); // кнопка save - доступна для формы караок
karaoke.footer().print(); // кнопка print - не доступен для формы караок

Это возвращает нас к моему вопросу выше (в другой теме) - про встроенные классы :smile: Не нужны они здесь

Если элемент переиспользуется на разных страницах, класс работы с этим элементом не должен быть встроен в страницу

Понял, спасибо.

God object - это всегда плохо :wink:

Так ведь общие элементы в одном классе которых как-раз может накопится до 50, уже по сути будет являться God Object, разве нет?

Какие же они “общие” если

Вы тогда наверно имели в виду “Пихать все в один класс описывающий страницу” да? (в данной теме)

Хорошо, а допустим есть Footer с кнопками, который есть на главных страницах меню(караоке, видеофильмы итд) если мы откроем форму одной из этих страниц, то там есть тоже Footer со своими кнопками, а внутри формы есть Popup в котором тоже есть Footer и тоже со своими отличными кнопками. То есть 3 разные страницы со своим Footer. Как быть в такой ситуации?

Если на разных страницах разные футеры, значит они должны быть представлены разными PageObject-классами фреймворка. Причём, если этот футер уникален для этой конкретной страницы (на других страницах не используется), то его вообще нет необходимости выносить в отдельный класс, а реализовывать в классе самой страницы

Иногда декомпозиция (классы, иннер классы) имеет место быть, для уменьшения нервов и износа скролла мыши.

1 лайк

Можете глянуть в сторону использования фреймворка html elements

Хотя в нем есть конечно свои недочеты, но как вариант - можно посмотреть и почерпнуть из него что-то для себя

Решил вынести в отдельный класс Footer для форм, так как кнопки там одинаковые. И опять сразу возникла проблема (по видео с Михаилом по PageObject) говорится что мы в каждом методе должны возвращать объект страницы с которой продолжаем работать. В следующем случае я должен возвращать, что-то после метода public void save() ?

public class Footer{
          public void save(){
}
}

public class Karaoke extends Page{
          public Footer button(){
             return new Footer(driver);
}
}

public class Movie extends Page{
          public Footer button(){
             return new Footer(driver);
}
}

По-хорошему, да