โมเดลวัตถุหน้าและโรงงานใน 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
- รูปแบบการออกแบบวัตถุในหน้าเพจระบุว่าการดำเนินการและโฟลว์ใน UI ควรแยกออกจากการตรวจสอบ แนวคิดนี้ทำให้โค้ดของเราสะอาดขึ้นและเข้าใจง่ายขึ้น
- ประโยชน์ประการที่สองคือที่เก็บวัตถุนั้นไม่ขึ้นอยู่กับกรณีทดสอบ ดังนั้นเราจึงสามารถใช้ที่เก็บวัตถุเดียวกันเพื่อวัตถุประสงค์ที่แตกต่างกันด้วยเครื่องมือที่แตกต่างกัน ตัวอย่างเช่น เราสามารถรวม Page Object Model เข้าไปได้ Selenium กับ TestNG/JUnit สำหรับการทำงาน การทดสอบ และในเวลาเดียวกันกับ JBehave/Cucumber สำหรับการทดสอบการยอมรับ
- โค้ดน้อยลงและได้รับการปรับปรุงให้เหมาะสมเนื่องจากวิธีการเพจแบบใช้ซ้ำได้ในคลาส POM
- วิธีการได้รับชื่อที่สมจริงมากขึ้นซึ่งสามารถแมปได้อย่างง่ายดายด้วยการดำเนินการที่เกิดขึ้นใน UI เช่น หากหลังจากคลิกปุ่มแล้วเราลงเอยที่หน้าแรก ชื่อวิธีการจะเป็นเช่น 'gotoHomePage()'
จะนำ POM ไปใช้อย่างไร?
POM ง่าย ๆ:
เป็นโครงสร้างพื้นฐานของกรอบงานโมเดลออบเจ็กต์เพจโดยที่องค์ประกอบเว็บทั้งหมดของ AUT และวิธีการที่ดำเนินการบนองค์ประกอบเว็บเหล่านี้จะได้รับการดูแลรักษาภายในไฟล์คลาส งานเช่นการตรวจสอบควรแยกออกจากกันเป็นส่วนหนึ่งของวิธีการทดสอบ
ตัวอย่างที่สมบูรณ์
กรณีทดสอบ: ไปที่เว็บไซต์สาธิต Guru99
ขั้นตอนที่ 1) ไปที่ไซต์สาธิต Guru99
ขั้นตอนที่ 2) ในหน้าแรกให้ตรวจสอบข้อความ “Guru99 Bank” ปรากฏอยู่
ขั้นตอนที่ 3) เข้าสู่ระบบแอปพลิเคชัน
ขั้นตอนที่ 4) ตรวจสอบว่าหน้าแรกมีข้อความเป็น “Manger Id: demo”
นี่คือเรากำลังจัดการกับ 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 เพื่อเริ่มต้นองค์ประกอบเว็บ
@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")); } }
โครงสร้างโครงการที่สมบูรณ์จะมีลักษณะเหมือนแผนภาพ:
โรงงาน AjaxElementLocator
โรงงาน AjaxElementLocator เป็นแนวคิดการโหลดแบบ Lazy Loading ของ PageFactory ใน Seleniumใช้เพื่อระบุองค์ประกอบเว็บเฉพาะในกรณีที่องค์ประกอบนั้นถูกใช้ในการดำเนินการใดๆ เท่านั้น โดยจะกำหนดเวลาหมดเวลาสำหรับ WebElements ให้กับคลาสเพจของอ็อบเจกต์ ข้อดีหลักประการหนึ่งของการใช้รูปแบบ PageFactory ใน Selenium เป็นคลาส AjaxElementLocatorFactory
ที่นี่ เมื่อดำเนินการกับองค์ประกอบ การรอให้องค์ประกอบนั้นมองเห็นได้จะเริ่มตั้งแต่ช่วงเวลานั้นเท่านั้น หากไม่พบองค์ประกอบนั้นในช่วงเวลาที่กำหนด กรณีทดสอบ การดำเนินการจะส่งข้อยกเว้น 'NoSuchElementException'
สรุป
- โมเดลออบเจ็กต์เพจใน Selenium WebDriver เป็นรูปแบบการออกแบบ Object Repository
- Selenium โมเดลออบเจ็กต์เพจสร้างโค้ดทดสอบของเราที่สามารถบำรุงรักษาและนำกลับมาใช้ใหม่ได้
- Page Factory เป็นวิธีที่เหมาะสมที่สุดในการสร้างที่เก็บอ็อบเจ็กต์ในแนวคิดเฟรมเวิร์ก Page Object Model
- AjaxElementLocatorFactory เป็นแนวคิดการโหลดแบบขี้เกียจใน Page Factory ซึ่งเป็นรูปแบบการออกแบบวัตถุของหน้าที่จะระบุ WebElements เฉพาะเมื่อมีการใช้ในการดำเนินการใดๆ เท่านั้น