Page Object Model (POM) и Page Factory в Selenium

Какво е обектен модел на страница?

Page Object Model (POM) е шаблон за проектиране, широко използван в автоматизацията на тестовете, който създава Object Repository за елементи на уеб интерфейса. Предимството на модела е, че намалява дублирането на код и подобрява поддръжката на теста.

При този модел за всяка уеб страница в приложението трябва да има съответен клас страница. Този клас Page ще идентифицира WebElements на тази уеб страница и също така съдържа Page методи, които извършват операции върху тези WebElements. Името на тези методи трябва да бъде дадено според задачата, която изпълняват, т.е. ако товарачът чака да се появи платежният шлюз, името на POM метода може да бъде waitForPaymentScreenDisplay().

Обектен модел на страницата

Защо Page Object Model?

Стартиране на автоматизация на потребителския интерфейс в Selenium WebDriver НЕ е трудна задача. Просто трябва да намерите елементи, да извършите операции върху него.

Помислете за този прост скрипт, за да влезете в уебсайт

Обектен модел на страницата

Както можете да видите, всичко, което правим, е да намираме елементи и да попълваме стойности за тези елементи.

Това е малък скрипт. Поддръжката на скрипт изглежда лесна. Но с времето наборът от тестове ще расте. Докато добавяте все повече и повече редове към кода си, нещата стават трудни.

Основният проблем с поддръжката на скриптове е, че ако 10 различни скрипта използват един и същ елемент на страницата, с всяка промяна в този елемент, трябва да промените всичките 10 скрипта. Това отнема време и е податливо на грешки.

По-добър подход към поддръжката на скрипта е да се създаде отделен клас файл, който да намира уеб елементи, да ги попълва или проверява. Този клас може да се използва повторно във всички скриптове, използващи този елемент. В бъдеще, ако има промяна в уеб елемента, трябва да направим промяната само в 1 клас файл, а не в 10 различни скрипта.

Този подход се нарича Page Object Model in Selenium. Това помага да се направи кодът по-четлив, поддържаем и многократно използваем.

Обектен модел на страницата

Предимства на POM

  1. Шаблонът за проектиране на обект на страница казва, че операциите и потоците в потребителския интерфейс трябва да бъдат отделени от проверката. Тази концепция прави нашия код по-чист и лесен за разбиране.
  2. Второто предимство е, че хранилището на обекти е независимо от тестови случаи, така че можем да използваме едно и също хранилище на обекти за различни цели с различни инструменти. Например, можем да интегрираме Page Object Model в Selenium с TestNG/JUnit за функционални Тестване и в същото време с JBehave/Cucumber за тестване за приемане.
  3. Кодът става по-малко и оптимизиран поради методите за повторно използване на страници в POM класовете.
  4. Методите получават по-реалистични имена, които могат лесно да бъдат съпоставени с операцията, извършвана в потребителския интерфейс. т.е. ако след щракване върху бутона стигнем до началната страница, името на метода ще бъде като 'gotoHomePage()'.

Как да внедрим POM?

Обикновен POM:

Това е основната структура на рамката на обектния модел на страницата, където всички уеб елементи на AUT и методът, който работи с тези уеб елементи, се поддържа във файл на клас. Задача като проверка трябва да бъде отделна като част от тестовите методи.

Внедрете POM

Пълен пример

тестов случай: Отидете на демонстрационния сайт на Guru99.

Стъпка 1) Отидете на Guru99 Demo Site

Внедрете POM

Стъпка 2) В началната страница проверете текста „Guru99 Bank“.

Внедрете POM

Стъпка 3) Влезте в приложението

Внедрете POM

Стъпка 4) Проверете дали началната страница съдържа текст като „Идентификационен номер на мениджър: демонстрация“

Внедрете POM

Тук имаме работа с 2 страници

  1. Логин страница
  2. Начална страница (показва се след като влезете)

Съответно създаваме 2 POM в Selenium класове

Guru99 Страница за вход POM

package pages;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

public class Guru99Login {

    WebDriver driver;

    By user99GuruName = By.name("uid");

    By password99Guru = By.name("password");

    By titleText =By.className("barone");

    By login = By.name("btnLogin");

    public Guru99Login(WebDriver driver){

        this.driver = driver;

    }

    //Set user name in textbox

    public void setUserName(String strUserName){

        driver.findElement(user99GuruName).sendKeys(strUserName);

    }

    //Set password in password textbox

    public void setPassword(String strPassword){

         driver.findElement(password99Guru).sendKeys(strPassword);

    }

    //Click on login button

    public void clickLogin(){

            driver.findElement(login).click();

    }

    //Get the title of Login Page

    public String getLoginTitle(){

     return    driver.findElement(titleText).getText();

    }

    /**

     * This POM method will be exposed in test case to login in the application

     * @param strUserName

     * @param strPasword

     * @return

     */

    public void loginToGuru99(String strUserName,String strPasword){

        //Fill user name

        this.setUserName(strUserName);

        //Fill password

        this.setPassword(strPasword);

        //Click Login button

        this.clickLogin();        
    }

}

Guru99 Начална страница POM в Selenium

package pages;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

public class Guru99HomePage {

    WebDriver driver;

    By homePageUserName = By.xpath("//table//tr[@class='heading3']");

    

    public Guru99HomePage(WebDriver driver){

        this.driver = driver;

    }

    //Get the User name from Home Page

        public String getHomePageDashboardUserName(){

         return    driver.findElement(homePageUserName).getText();

        }

}

Guru99 Обикновено POM в Selenium Тестов случай

package test;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.testng.Assert;

import org.testng.annotations.BeforeTest;

import org.testng.annotations.Test;

import pages.Guru99HomePage;

import pages.Guru99Login;

public class Test99GuruLogin {

    String driverPath = "C:\\geckodriver.exe";
    
    WebDriver driver;

    Guru99Login objLogin;

    Guru99HomePage objHomePage;

    @BeforeTest

    public void setup(){

	System.setProperty("webdriver.gecko.driver", driverPath);
        
        driver = new FirefoxDriver();

        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

        driver.get("http://demo.guru99.com/V4/");

    }

    /**

     * This test case will login in http://demo.guru99.com/V4/

     * Verify login page title as guru99 bank

     * Login to application

     * Verify the home page using Dashboard message

     */

    @Test(priority=0)

    public void test_Home_Page_Appear_Correct(){

        //Create Login Page object

    objLogin = new Guru99Login(driver);

    //Verify login page title

    String loginPageTitle = objLogin.getLoginTitle();

    Assert.assertTrue(loginPageTitle.toLowerCase().contains("guru99 bank"));

    //login to application

    objLogin.loginToGuru99("mgr123", "mgr!23");

    // go the next page

    objHomePage = new Guru99HomePage(driver);

    //Verify home page

    Assert.assertTrue(objHomePage.getHomePageDashboardUserName().toLowerCase().contains("manger id : mgr123"));

    }

В какво е Page Factory Selenium?

Page Factory в Selenium е вградена концепция за рамка на Page Object Model за Selenium WebDriver, но е много оптимизиран. Използва се за инициализация на обекти Page или за инстанциране на самия обект Page. Използва се и за инициализиране на елементи от клас на страница без използване на „FindElement/s“.

Тук също следваме концепцията за разделяне на Page Object Repository и Test Methods. Освен това, с помощта на клас PageFactory в Selenium, използваме анотации @FindBy за да намерите WebElement. Използваме метода initElements за инициализиране на уеб елементи

Page Factory In Selenium

@FindBy може да приеме tagName, partialLinkText, име, linkText, id, css, className, xpath като атрибути.

Нека разгледаме същия пример като по-горе, използвайки Page Factory

Guru99 Страница за вход с Page Factory

package PageFactory;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.FindBy;

import org.openqa.selenium.support.PageFactory;

public class Guru99Login {

    /**

     * All WebElements are identified by @FindBy annotation

     */

    WebDriver driver;

    @FindBy(name="uid")

    WebElement user99GuruName;

    @FindBy(name="password")

    WebElement password99Guru;    

    @FindBy(className="barone")

    WebElement titleText;

    @FindBy(name="btnLogin")

    WebElement login;

    public Guru99Login(WebDriver driver){

        this.driver = driver;

        //This initElements method will create all WebElements

        PageFactory.initElements(driver, this);

    }

    //Set user name in textbox

    public void setUserName(String strUserName){

        user99GuruName.sendKeys(strUserName);     
    }

    //Set password in password textbox

    public void setPassword(String strPassword){

    password99Guru.sendKeys(strPassword);

    }

    //Click on login button

    public void clickLogin(){

            login.click();

    }  

    //Get the title of Login Page

    public String getLoginTitle(){

     return    titleText.getText();

    }

    /**

     * This POM method will be exposed in test case to login in the application

     * @param strUserName

     * @param strPasword

     * @return

     */

    public void loginToGuru99(String strUserName,String strPasword){

        //Fill user name

        this.setUserName(strUserName);

        //Fill password

        this.setPassword(strPasword);

        //Click Login button

        this.clickLogin();           

    }

}

Начална страница Guru99 с фабрика за страници

package PageFactory;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.FindBy;

import org.openqa.selenium.support.PageFactory;

public class Guru99HomePage {

    WebDriver driver;

    @FindBy(xpath="//table//tr[@class='heading3']")

    WebElement homePageUserName;    

    public Guru99HomePage(WebDriver driver){

        this.driver = driver;

        //This initElements method will create all WebElements

        PageFactory.initElements(driver, this);

    }   

    //Get the User name from Home Page

        public String getHomePageDashboardUserName(){

         return    homePageUserName.getText();

        }

}

Guru99 TestCase с концепция Page Factory

package test;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.testng.Assert;

import org.testng.annotations.BeforeTest;

import org.testng.annotations.Test;

import PageFactory.Guru99HomePage;

import PageFactory.Guru99Login;

public class Test99GuruLoginWithPageFactory {

    String driverPath = "C:\\geckodriver.exe";
    
    WebDriver driver;

    Guru99Login objLogin;

    Guru99HomePage objHomePage; 

    @BeforeTest

    public void setup(){

        System.setProperty("webdriver.gecko.driver", driverPath);
        
        driver = new FirefoxDriver();

        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

        driver.get("http://demo.guru99.com/V4/");

    }

    /**

     * This test go to http://demo.guru99.com/V4/

     * Verify login page title as guru99 bank

     * Login to application

     * Verify the home page using Dashboard message

     */

    @Test(priority=0)

    public void test_Home_Page_Appear_Correct(){

        //Create Login Page object

    objLogin = new Guru99Login(driver);

    //Verify login page title

    String loginPageTitle = objLogin.getLoginTitle();

    Assert.assertTrue(loginPageTitle.toLowerCase().contains("guru99 bank"));

    //login to application

    objLogin.loginToGuru99("mgr123", "mgr!23");

    // go the next page

    objHomePage = new Guru99HomePage(driver);

    //Verify home page

    Assert.assertTrue(objHomePage.getHomePageDashboardUserName().toLowerCase().contains("manger id : mgr123"));

    }

}

Пълната структура на проекта ще изглежда като диаграмата:

Page Factory в Selenium

AjaxElementLocatorFactory

AjaxElementLocatorFactory е концепция за мързеливо зареждане на PageFactory в Selenium. Използва се за намиране на уеб елементи само когато елементите се използват в някаква операция. Той присвоява таймаут за WebElements на класа страница на обекта. Едно от ключовите предимства на използването на модела PageFactory в Selenium е AjaxElementLocatorFactory клас.

Тук, когато се извърши операция върху елемент, изчакването за неговата видимост започва само от този момент. Ако елементът не бъде намерен в дадения интервал от време, Тестов случай изпълнението ще хвърли изключение „NoSuchElementException“.

Фабрика за локатор на AjaxElement

Oбобщение

  1. Page Object Model in Selenium WebDriver е шаблон за проектиране на обектно хранилище.
  2. Selenium обектният модел на страницата създава нашия код за тестване, който може да се поддържа и използва повторно.
  3. Page Factory е оптимизиран начин за създаване на хранилище на обекти в концепцията на рамката на Page Object Model.
  4. AjaxElementLocatorFactory е концепция за отложено зареждане в Page Factory – шаблон за проектиране на обект на страница за идентифициране на WebElements само когато се използват в която и да е операция.

Изтеглете Selenium Проектни файлове за демонстрацията в този урок