Есть отличная удаленная работа для php+codeception+jenkins+allure+docker спецов. 100% remote! Присоединиться к проекту

PageObject header footer и другие не меняющиеся элементы

page-object
webdriver
java
Теги: #<Tag:0x00007f7b642b8a90> #<Tag:0x00007f7b642b8928> #<Tag:0x00007f7b642b87e8>

(Андрей Щербаков) #1

Я пытаюсь реализовать PageObject для автоматизации интернет магазина. Почти на всех страницах есть не меняющийся header,footer и еще некоторые элементы.
Как лучше всего их реализовать и куда запихнуть?

Summary

Лучшее, что пришло мне в голову это унаследовать от BasePage 1 промежуточный класс и в нем создать экземпляры классов Header,Footer и т.д. Правда тогда придется в тестах получать обьект header для обращения к его методам.
Либо сделать эти классы вложенными,но мне кажется что от этого станет только хуже
Есть еще несколько вариантов,но все кажутся неправильными по архитектуре


(Taras) #2

HTMLElements Вам в помощь


(Oleksii Ihnatiuk) #3

Можно создавать экземпляры хедера и футера в конструкторе других пейджей. Таким образом можно будет внутри вашей страницы писать методы по типу:

public LogInPage goToLoginPage{
return this.clickOnLoginButton();
}

а сам метод clickOnLoginButton() будет у вас в классе хедера например, и уже будет возвращать новую страницу LoginPage.


(Андрей Щербаков) #4

Подскажите,что я делаю не так
допустим я создал “расширенную страницу”

public abstract class ExtendedPage extends Page{

protected Header header;

public ExtendedPage(WebDriver webDriver) {
	super(webDriver);
	header=new Header(webDriver);
}

public Header getHeader(){
	return header;
}

HomePage наследуется от ExtendedPage
но для использования методов хедера в тестах мне приходится делать что-то наподобие

public class DeliveryPageTest extends TestBase{

HomePage homepage;

@Parameters({ "path" })
@BeforeClass
public void testInit(String path) {
	homepage=new HomePage(webDriver);
	homepage.**getHeader()**.clickDeliveryButton();
}

И еще вопрос,в хедере есть смена страны,т.е. я могу поменять ее на многих страницах,при этом url не меняется,переходов не происходит.Что я должен возвращать, чтобы получить тот класс,на котором происходило это действие?
Я подумал что придется писать метод для смены страны в родительском BasePage,чтобы не взаимодействовать с хередом, но все это начинает походить на лютый говнокод


(Oleksii Ihnatiuk) #5
  1. объявлять драйвер для каждой страницы не есть хорошо
  2. я бы убрал строчку

protected Header header

и перенес бы это в конструктор

public ExtendedPage(WebDriver webDriver) {
super(webDriver);
Header header = new Header(webDriver);
}

для того, чтобы убрать прослойку getHeader вы должны реализовать public методы в самом HomePage классе.

То что я предложил избавит вас от копипаста всех локаторов в хедери и private методов. То есть в других класах вы будете описывать методы, которые будут использовать public методы Header пейджи.


(Андрей Щербаков) #6

webDriver у меня обьявлен только в BasePage

public abstract class Page {
protected WebDriver webDriver;
public Page(WebDriver webDriver) {
PageFactory.initElements(webDriver, this); //хотя читал что так делать не советуют
this.webDriver = webDriver;
}

Я возможно не правильно понял,но если реализовывать методы Header в HomePage,то мне придется реализовывать их еще в 10-15 страницах, а я как раз хотел избавится от дублирования кода
Т.е. насколько правильно вообще в каждую страницу заносить экземпляры хедера,футера и т.д.?
Как по мне будет лучше сделать что-то наподобие
BasePage>ExtendedPage>HomePage и еще 10 подобных страниц
BasePage>ExtendedPage>StaticPage>DeliveryPage
BasePage>CartPage(в CartPage отсутствует хедер и футер в таком виде как на других страницах)
В ExtendedPage реализовать методы футера и хедера(или вообще опустить создание хедера и футера)
Тогда у всех страниц будет общий родитель BasePage,а взаимодействовать с методами хедера и футера я смогу только на тех страницах,где они есть

Надеюсь я не несу чушь :slight_smile:


(Oleksii Ihnatiuk) #7

в ваших словах все правильно :slight_smile:
думаю проблема в синтаксисе
вечером гляну


(Oleksii Ihnatiuk) #8

Есть абстрактный BaseClass в котором реализована инициализация драйвера и вейтера и методы, к которым я хочу чтобы был доступ у всех пейджей.
Потом я реализовал абстрактный класс по типу ExtendedPage:

@Getter
public abstract class HeaderFooter extends BaseClass{

 private final Header header = new Header;
 private final Footer footer = new Footer;

}

для уменьшения Boilerplate кода я использую библиотеку lombok, если быть точнее, то lombok-intellij-plugin .

Без него это выглядело б так:

public abstract class HeaderFooter extends BaseClass {

private final Header header = new Header();
private final Footer footer = new Footer();
public Header getHeader() {
    return header;
}
public Footer getFooter() {
    return footer;
}

}

В нужном мне пейдже я extend уже не Базовый класс а расширенный. И больше в нем писать ничего не нужно.

В самих тестах мы получаем:

  • было:
@Test
public void methodName(){
    homePage.goToTreatmentRatingsConditionPage()
                    .goToTreatmentVideosPage();
    header.goToSignUpPage()
            .checkUrl()
            .checkIsJoinNowButtonExists()
            .checkIsSignUpWithFacebookButtonExists();
}
  • стало
@Test
public void methodName(){
    homePage.goToTreatmentRatingsConditionPage()
            .goToTreatmentVideosPage()
            .getHeader() //то как мы вызываем экземпляр класса Header
            .goToSignUpPage()
            .checkUrl()
            .checkIsJoinNowButtonExists()
            .checkIsSignUpWithFacebookButtonExists();
}

Как вы видете добавилось только getHeader() в самом тесте. Надеюсь, что помог вам.


(Oleksii Ihnatiuk) #9

Андрей Щербаков если мой код вам помог, то пометьте сообщение как “Ответ”. Спасибо


(Eugene Moskalenko) #10

Зачем же так все усложнять? :slight_smile:

У вас футер и хедер встречаются на всех страницах, значит и в вашем коде они должны без проблем вызываться, вызывая обьект страниц.

Я бы сделал так. У вас ведь все классы пейджов - наследуются от базового класса. Вот в базовом классе и внесите логику касательно хедеров и футеров. Там по сути все такое и должно лежать, а не методы с кликами и прочей фигней…

Таким образом у всех наследников от базового класса - будет возможность юзать логику касательно футера и хедера.

Еще можно сделать такой костыль. По сути любая страница - это обьект, тогда в базовом классе можно добавить два свойства:

public abstract class BasePage {
    public Header header;
    public Footer footer;
}

в этих классах: Header, Footer - сделать конструктор, и тогда, когда будете вызывать свои методы для какой-то из страниц, то можно будет вызвать их так:

@Test()
public void someTest() {
    // --------------------- Test Data ----------------------//

    // --------------------- Test Case ----------------------//
    HomePage homePage = new HomePage();
    homPage.header.getSomthingMethodFromHeaderClass();
    homPage.footer.getSomthingMethodFromFooterClass()
}

Сам такое еще не пробовал, только что в голову пришло. Надо будет как-то попробовать, но работать должно.

Но я бы советовал всю общую логику касательно всех пейджей - складывать в базовый родительский класс, он для этого и нужен, чтобы там были собраны методы и поля, которые необходимы дочерним классам. С хедером и футером - как раз именно такая история. Они есть на всех страницах, значит должна быть возможность дернуть их из разных пейджей… При этом чтобы там небыло мусора, стоит вынести оттуда клики, ожидания и выполнения js методы в другие классы.


(Андрей Щербаков) #11

Все дело в том, что хеадер и футер отсутствует на некоторых страницах,я писал об это в начале.Более того на некоторых страницах добавляется sidebar,также одинаковый для этих страниц(не для всех).
Делать публичными header footer можно конечно,правда это нарушает инкапсуляцию, в целом ничем не лучше писать homPage.header.getSomthingMethodFromHeaderClass(); чем homPage.getHeader().getSomthingMethodFromHeaderClass();
В целом вопрос заключался в том,как написать архитектуру так,чтобы работать со страницей без .getHeader .
homepage.doSomethingFromHeader .Либо вообще не создавать классы хедер футер,просто интересуюсь как люди организуют свои проекты

Все общие методы я и так складываю в BasePage и BaseTest,а так же во вспомогательные классы


(Sergey Korol) #12

4 дня обсуждаете то, что заложено в основах ООП. Советую почитать на досуге темы Inheritance / Composition over inheritance, и сделать выводы относительно того, какой подход наилучшим образом подойдет для решения вашей задачи. Более того, вопрос - куда девать геттеры - отпадет сам собой. :wink:


(Sergey QA) #13

Все дело в том, что хеадер и футер отсутствует на некоторых страницах,я писал об это в начале.

И б-г с ними!
Лично я реализовываю по классике, объявляя соответствующие элементы и методы для взаимодействия с ними на БэйсПэйдже - ну, будет у вас возможность вызывать эти методы со страницы, на которой их нет - так не вызывайте, такая “избыточность” никак не повлияет на работоспособность и скорость прохода тестов.


(Oleksii Ihnatiuk) #14

Большое спасибо ArtOfLife . Почитал, то что вы посоветовали. В итоге:

  • удалил избыточное наследование
  • создал экземпляры Header и Footer в пейдже, где они нужны и там же добавил Getter для них.

(Alexander Maximov) #15

Я тут тоже столкнулся недавно с этой задачей. У меня в итоге все общие панели являются свойствами главной страницы (если что, это C#):

    public class MainPage : MyPageBase
    {
        public MainPage()
        {
            LeftPanePage = new LeftPanePage();
            AccountSelectorPage = new AccountSelectorPage();
            OperationsPanePage = new OperationsPanePage();
            DictionaryPanePage = new DictionaryPanePage();
            DataPanePage = new DataPanePage();
        }

        public LeftPanePage LeftPanePage { get; private set; }
        public AccountSelectorPage AccountSelectorPage { get; private set; }
        public OperationsPanePage OperationsPanePage { get; private set; }
        public DictionaryPanePage DictionaryPanePage { get; private set; }
        public DataPanePage DataPanePage { get; private set; }
..........

Все остальные страницы я уже наследую от этой главной и тогда тесты выглядят следующим образом:

var groupPage = mainPage.LeftPanePage
                .OpenGroupPage();
...
mainPage.OperationsPanePage.ExpandIfCollapsed();
mainPage.DictionaryPanePage.ExpandIfCollapsed();
mainPage.OperationsPanePage.SelectFirstRow();
mainPage.DictionaryPanePage.OpenTabByName("Маршрут");

(Nick) #16

Зачем все инициализиовать в конструкторе? Lazy не?