โมเดลวัตถุหน้าและโรงงานใน Selenium

โมเดลออบเจ็กต์เพจคืออะไร

โมเดลออบเจ็กต์เพจ (POM) เป็นรูปแบบการออกแบบที่ใช้กันอย่างแพร่หลายในการทดสอบอัตโนมัติที่สร้าง Object Repository สำหรับองค์ประกอบ UI ของเว็บ ข้อดีของโมเดลนี้คือช่วยลดความซ้ำซ้อนของโค้ดและปรับปรุงการบำรุงรักษาการทดสอบ

ภายใต้โมเดลนี้ สำหรับแต่ละหน้าเว็บในแอปพลิเคชัน ควรมีคลาส Page ที่สอดคล้องกัน คลาส Page นี้จะระบุ WebElements ของหน้าเว็บนั้น และยังประกอบด้วยเมธอด Page ที่ดำเนินการกับ WebElements เหล่านั้นด้วย ชื่อของเมธอดเหล่านี้ควรกำหนดตามงานที่กำลังดำเนินการอยู่ กล่าวคือ หากตัวโหลดกำลังรอให้เกตเวย์การชำระเงินปรากฏขึ้น ชื่อเมธอด POM อาจเป็น waitForPaymentScreenDisplay()

โมเดลออบเจ็กต์เพจ

ทำไมต้องใช้โมเดลออบเจ็กต์เพจ?

การเริ่มต้น UI Automation ใน Selenium WebDriver ไม่ใช่เรื่องยาก คุณเพียงแค่ต้องค้นหาองค์ประกอบและดำเนินการกับองค์ประกอบเหล่านั้น

พิจารณาสคริปต์ง่ายๆ นี้เพื่อเข้าสู่เว็บไซต์

โมเดลออบเจ็กต์เพจ

อย่างที่คุณเห็น สิ่งที่เราทำคือการค้นหาองค์ประกอบและเติมค่าให้กับองค์ประกอบเหล่านั้น

นี่เป็นสคริปต์ขนาดเล็ก การบำรุงรักษาสคริปต์ดูง่าย แต่เมื่อเวลาผ่านไปชุดทดสอบก็จะเติบโตขึ้น เมื่อคุณเพิ่มบรรทัดในโค้ดของคุณมากขึ้นเรื่อยๆ สิ่งต่างๆ ก็จะกลายเป็นเรื่องยาก

ปัญหาหลักในการบำรุงรักษาสคริปต์ก็คือ หากมีสคริปต์ที่แตกต่างกัน 10 สคริปต์ใช้องค์ประกอบของหน้าเดียวกัน หากมีการเปลี่ยนแปลงในองค์ประกอบนั้น คุณจะต้องเปลี่ยนสคริปต์ทั้ง 10 สคริปต์ ซึ่งใช้เวลานานและเกิดข้อผิดพลาดได้ง่าย

แนวทางที่ดีกว่าในการบำรุงรักษาสคริปต์คือการสร้างไฟล์คลาสแยกต่างหากซึ่งจะค้นหาองค์ประกอบของเว็บ เติมองค์ประกอบเหล่านั้น หรือตรวจสอบองค์ประกอบเหล่านั้น คลาสนี้สามารถนำมาใช้ซ้ำได้ในสคริปต์ทั้งหมดโดยใช้องค์ประกอบนั้น ในอนาคต หากมีการเปลี่ยนแปลงในองค์ประกอบเว็บ เราจำเป็นต้องทำการเปลี่ยนแปลงในไฟล์คลาสเพียง 1 ไฟล์ ไม่ใช่ 10 สคริปต์ที่แตกต่างกัน

วิธีการนี้เรียกว่า Page Object Model in Seleniumช่วยให้โค้ดสามารถอ่านได้ง่ายขึ้น บำรุงรักษาได้ง่ายขึ้น และนำกลับมาใช้ซ้ำได้

โมเดลออบเจ็กต์เพจ

ข้อดีของ POM

  1. รูปแบบการออกแบบวัตถุในหน้าเพจระบุว่าการดำเนินการและโฟลว์ใน UI ควรแยกออกจากการตรวจสอบ แนวคิดนี้ทำให้โค้ดของเราสะอาดขึ้นและเข้าใจง่ายขึ้น
  2. ประโยชน์ประการที่สองคือที่เก็บวัตถุนั้นไม่ขึ้นอยู่กับกรณีทดสอบ ดังนั้นเราจึงสามารถใช้ที่เก็บวัตถุเดียวกันเพื่อวัตถุประสงค์ที่แตกต่างกันด้วยเครื่องมือที่แตกต่างกัน ตัวอย่างเช่น เราสามารถรวม Page Object Model เข้าไปได้ Selenium กับ TestNG/JUnit สำหรับการทำงาน การทดสอบ และในเวลาเดียวกันกับ JBehave/Cucumber สำหรับการทดสอบการยอมรับ
  3. โค้ดน้อยลงและได้รับการปรับปรุงให้เหมาะสมเนื่องจากวิธีการเพจแบบใช้ซ้ำได้ในคลาส POM
  4. วิธีการได้รับชื่อที่สมจริงมากขึ้นซึ่งสามารถแมปได้อย่างง่ายดายด้วยการดำเนินการที่เกิดขึ้นใน UI เช่น หากหลังจากคลิกปุ่มแล้วเราลงเอยที่หน้าแรก ชื่อวิธีการจะเป็นเช่น 'gotoHomePage()'

จะนำ POM ไปใช้อย่างไร?

POM ง่าย ๆ:

เป็นโครงสร้างพื้นฐานของกรอบงานโมเดลออบเจ็กต์เพจโดยที่องค์ประกอบเว็บทั้งหมดของ AUT และวิธีการที่ดำเนินการบนองค์ประกอบเว็บเหล่านี้จะได้รับการดูแลรักษาภายในไฟล์คลาส งานเช่นการตรวจสอบควรแยกออกจากกันเป็นส่วนหนึ่งของวิธีการทดสอบ

ปรับใช้ POM

ตัวอย่างที่สมบูรณ์

กรณีทดสอบ: ไปที่เว็บไซต์สาธิต Guru99

ขั้นตอนที่ 1) ไปที่ไซต์สาธิต Guru99

ปรับใช้ POM

ขั้นตอนที่ 2) ในหน้าแรกให้ตรวจสอบข้อความ “Guru99 Bank” ปรากฏอยู่

ปรับใช้ POM

ขั้นตอนที่ 3) เข้าสู่ระบบแอปพลิเคชัน

ปรับใช้ POM

ขั้นตอนที่ 4) ตรวจสอบว่าหน้าแรกมีข้อความเป็น “Manger Id: demo”

ปรับใช้ POM

นี่คือเรากำลังจัดการกับ 2 หน้า

  1. เข้าสู่ระบบหน้า
  2. หน้าแรก (แสดงเมื่อคุณเข้าสู่ระบบ)

ดังนั้นเราจึงสร้าง POM 2 อันขึ้นมา 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("https://demo.guru99.com/V4/");

    }

    /**

     * This test case will login in https://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?

หน้าโรงงานใน Selenium เป็นแนวคิดเฟรมเวิร์ก Page Object Model แบบ inbuilt สำหรับ Selenium WebDriver แต่ได้รับการปรับให้เหมาะสมที่สุด ใช้สำหรับการเริ่มต้นออบเจ็กต์เพจหรือเพื่อสร้างอินสแตนซ์ออบเจ็กต์เพจเอง นอกจากนี้ยังใช้เพื่อเริ่มต้นองค์ประกอบคลาสของหน้าโดยไม่ต้องใช้ “FindElement/s”

ในที่นี้ เรายังปฏิบัติตามแนวคิดของการแยก Page Object Repository และ Test Methods นอกจากนี้ด้วยความช่วยเหลือของคลาส PageFactory ใน Seleniumเราใช้คำอธิบายประกอบ @FindBy เพื่อค้นหา WebElement เราใช้วิธี initElements เพื่อเริ่มต้นองค์ประกอบเว็บ

เพจ โรงงาน อิน 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("https://demo.guru99.com/V4/");

    }

    /**

     * This test go to https://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"));

    }

}

โครงสร้างโครงการที่สมบูรณ์จะมีลักษณะเหมือนแผนภาพ:

หน้าโรงงานใน Selenium

โรงงาน AjaxElementLocator

โรงงาน AjaxElementLocator เป็นแนวคิดการโหลดแบบ Lazy Loading ของ PageFactory ใน Seleniumใช้เพื่อระบุองค์ประกอบเว็บเฉพาะในกรณีที่องค์ประกอบนั้นถูกใช้ในการดำเนินการใดๆ เท่านั้น โดยจะกำหนดเวลาหมดเวลาสำหรับ WebElements ให้กับคลาสเพจของอ็อบเจกต์ ข้อดีหลักประการหนึ่งของการใช้รูปแบบ PageFactory ใน Selenium เป็นคลาส AjaxElementLocatorFactory

ที่นี่ เมื่อดำเนินการกับองค์ประกอบ การรอให้องค์ประกอบนั้นมองเห็นได้จะเริ่มตั้งแต่ช่วงเวลานั้นเท่านั้น หากไม่พบองค์ประกอบนั้นในช่วงเวลาที่กำหนด กรณีทดสอบ การดำเนินการจะส่งข้อยกเว้น 'NoSuchElementException'

โรงงานตัวระบุตำแหน่ง AjaxElement

สรุป

  1. โมเดลออบเจ็กต์เพจใน Selenium WebDriver เป็นรูปแบบการออกแบบ Object Repository
  2. Selenium โมเดลออบเจ็กต์เพจสร้างโค้ดทดสอบของเราที่สามารถบำรุงรักษาและนำกลับมาใช้ใหม่ได้
  3. Page Factory เป็นวิธีที่เหมาะสมที่สุดในการสร้างที่เก็บอ็อบเจ็กต์ในแนวคิดเฟรมเวิร์ก Page Object Model
  4. AjaxElementLocatorFactory เป็นแนวคิดการโหลดแบบขี้เกียจใน Page Factory ซึ่งเป็นรูปแบบการออกแบบวัตถุของหน้าที่จะระบุ WebElements เฉพาะเมื่อมีการใช้ในการดำเนินการใดๆ เท่านั้น

ดาวน์โหลด Selenium ไฟล์โครงการสำหรับการสาธิตในบทช่วยสอนนี้

อ่านเพิ่มเติม readmore