ระบบอัตโนมัติในซีลีเนียม: Page Object Model และ Page Factory
เผยแพร่แล้ว: 2022-03-11การเขียนการทดสอบอัตโนมัติเป็นมากกว่าแค่ความหรูหราสำหรับทีมพัฒนาซอฟต์แวร์ที่คล่องตัว เป็นความต้องการและเป็นเครื่องมือสำคัญในการค้นหาจุดบกพร่องอย่างรวดเร็วในช่วงเริ่มต้นของวงจรการพัฒนาซอฟต์แวร์ เมื่อมีคุณลักษณะใหม่ที่ยังอยู่ในขั้นตอนการพัฒนา นักพัฒนาสามารถเรียกใช้การทดสอบอัตโนมัติและดูว่าส่วนอื่นๆ ของระบบได้รับผลกระทบจากการเปลี่ยนแปลงเหล่านั้นอย่างไร บทความนี้จะอธิบายวิธีเร่งความเร็วการทดสอบอัตโนมัติโดยใช้โมเดล Page Object ใน Selenium
การทดสอบแบบอัตโนมัติทำให้สามารถลดต้นทุนการแก้ไขข้อผิดพลาดและนำการปรับปรุงโดยรวมมาสู่กระบวนการประกันคุณภาพซอฟต์แวร์ (QA) ด้วยการทดสอบที่เหมาะสม นักพัฒนาจะได้รับโอกาสในการค้นหาและแก้ไขจุดบกพร่องก่อนที่จะถึง QA การทดสอบอัตโนมัติยังช่วยให้เราสร้างกรณีทดสอบและคุณลักษณะที่ถดถอยอย่างต่อเนื่องโดยอัตโนมัติ วิธีนี้จะช่วยให้ QA มีเวลามากขึ้นในการทดสอบส่วนอื่นๆ ของแอปพลิเคชัน นอกจากนี้ยังช่วยในการรับรองคุณภาพของผลิตภัณฑ์ในการเปิดตัวการผลิต เป็นผลให้เราได้รับผลิตภัณฑ์ที่มีเสถียรภาพมากขึ้นและกระบวนการ QA ที่มีประสิทธิภาพมากขึ้น
แม้ว่าการเขียนการทดสอบอัตโนมัติอาจดูเหมือนเป็นเรื่องง่ายสำหรับนักพัฒนาและวิศวกร แต่ก็ยังมีความเป็นไปได้ที่จะจบลงด้วยการทดสอบที่ใช้งานไม่ดี และค่าใช้จ่ายในการบำรุงรักษาโค้ดที่สูงในกระบวนการที่คล่องตัว การพยายามนำเสนอการเปลี่ยนแปลงหรือคุณสมบัติอย่างต่อเนื่องในโครงการพัฒนาที่คล่องตัวสามารถพิสูจน์ได้ว่ามีค่าใช้จ่ายสูงเมื่อมีการทดสอบ การเปลี่ยนองค์ประกอบหนึ่งรายการบนหน้าเว็บที่ต้องใช้การทดสอบ 20 รายการจะต้องดำเนินการทดสอบ 20 รายการและอัปเดตแต่ละรายการเพื่อปรับให้เข้ากับการเปลี่ยนแปลงที่เพิ่งแนะนำนี้ การทำเช่นนี้ไม่เพียงแต่จะใช้เวลานานมากเท่านั้น แต่ยังเป็นปัจจัยที่สร้างแรงจูงใจอย่างมากเมื่อต้องดำเนินการทดสอบอัตโนมัติตั้งแต่เนิ่นๆ
แต่ถ้าเราทำการเปลี่ยนแปลงได้ในที่เดียวเท่านั้น และให้รูทีนการทดสอบที่เกี่ยวข้องทุกชุดใช้งานได้หรือไม่ ในบทความนี้ เราจะมาดูการทดสอบอัตโนมัติใน Selenium และวิธีที่เราสามารถใช้แบบจำลอง Page Object เพื่อเขียนรูทีนการทดสอบที่บำรุงรักษาและนำกลับมาใช้ใหม่ได้
Page Object Model ในซีลีเนียม
Page Object model เป็นรูปแบบการออกแบบอ็อบเจ็กต์ใน Selenium โดยที่หน้าเว็บจะถูกแสดงเป็นคลาส และองค์ประกอบต่างๆ บนเพจถูกกำหนดเป็นตัวแปรในคลาส การโต้ตอบกับผู้ใช้ที่เป็นไปได้ทั้งหมดสามารถนำไปใช้เป็นเมธอดในคลาสได้:
clickLoginButton(); setCredentials(user_name,user_password);
เนื่องจากเมธอดที่มีชื่อดีในชั้นเรียนนั้นอ่านง่าย วิธีนี้จึงเป็นวิธีที่ดีเยี่ยมในการปรับใช้รูทีนการทดสอบที่ทั้งอ่านได้และง่ายต่อการบำรุงรักษาหรืออัปเดตในอนาคต ตัวอย่างเช่น:
เพื่อรองรับโมเดล Page Object เราใช้ Page Factory Page Factory ใน Selenium เป็นส่วนขยายของ Page Object และสามารถใช้งานได้หลายวิธี ในกรณีนี้ เราจะใช้ Page Factory เพื่อเริ่มต้นองค์ประกอบเว็บที่กำหนดไว้ในคลาสของหน้าเว็บหรือ Page Objects
คลาสของเว็บเพจหรือ Page Objects ที่มีองค์ประกอบของเว็บจำเป็นต้องเริ่มต้นโดยใช้ Page Factory ก่อนจึงจะสามารถใช้ตัวแปรองค์ประกอบเว็บได้ สามารถทำได้โดยใช้ฟังก์ชัน initElements บน PageFactory:
LoginPage page = new LoginPage(driver); PageFactory.initElements(driver, page);
หรือง่ายกว่านั้น:
LoginPage page = PageFactory.intElements(driver,LoginPage.class)
หรือภายในตัวสร้างคลาสของหน้าเว็บ:
public LoginPage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); }
Page Factory จะเริ่มต้นทุกตัวแปร WebElement ด้วยการอ้างอิงไปยังองค์ประกอบที่เกี่ยวข้องบนหน้าเว็บจริงตาม "locators" ที่กำหนดค่าไว้ ทำได้โดยใช้คำอธิบายประกอบ @FindBy ด้วยคำอธิบายประกอบนี้ เราสามารถกำหนดกลยุทธ์ในการค้นหาองค์ประกอบ พร้อมด้วยข้อมูลที่จำเป็นสำหรับการระบุองค์ประกอบ:
@FindBy(how=How.NAME, using="username") private WebElement user_name;
ทุกครั้งที่มีการเรียกใช้เมธอดบนตัวแปร WebElement นี้ อันดับแรก ไดรเวอร์จะพบมันในหน้าปัจจุบัน จากนั้นจึงจำลองการโต้ตอบ ในกรณีที่เราทำงานกับเพจธรรมดาๆ เรารู้ว่าเราจะพบองค์ประกอบบนเพจทุกครั้งที่เราค้นหามัน และเรารู้ด้วยว่าในที่สุดเราจะออกจากหน้านี้และไม่กลับมาที่หน้านี้ เราสามารถแคชได้ ฟิลด์ค้นหาโดยใช้คำอธิบายประกอบง่ายๆ อื่น:
@FindBy(how=How.NAME, using="username") @CacheLookup private WebElement user_name;
คำจำกัดความทั้งหมดของตัวแปร WebElement สามารถแทนที่ด้วยรูปแบบที่กระชับยิ่งขึ้น:
@FindBy(name="username") private WebElement user_name;
คำอธิบายประกอบ @FindBy รองรับกลยุทธ์อื่นๆ จำนวนหนึ่งที่ทำให้สิ่งต่างๆ ง่ายขึ้นเล็กน้อย:
id, name, className, css, tagName, linkText, partialLinkText, xpath
@FindBy() private WebElement user_name; @FindBy(name="passsword") private WebElement user_password; @FindBy(className="h3") private WebElement label; @FindBy(css=”#content”) private WebElement text;
เมื่อเริ่มต้นแล้ว ตัวแปร WebElement เหล่านี้สามารถใช้โต้ตอบกับองค์ประกอบที่เกี่ยวข้องบนหน้าได้ รหัสต่อไปนี้จะ ตัวอย่างเช่น:
user_password.sendKeys(password);
… ส่งลำดับของการกดแป้นที่กำหนดไปยังฟิลด์รหัสผ่านบนหน้า และเทียบเท่ากับ:
driver.findElement(By.name(“user_password”)).sendKeys(password);
ต่อไป คุณมักจะเจอสถานการณ์ที่คุณต้องการค้นหารายการองค์ประกอบบนหน้า และนั่นคือเมื่อ @FindBys มีประโยชน์:
@FindBys(@FindBy(css=”div[class='yt-lockup-tile yt-lockup-video']”))) private List<WebElement> videoElements;
โค้ดด้านบนจะค้นหาองค์ประกอบ div ทั้งหมดที่มีชื่อคลาสสองชื่อ "yt-lockup-tile" และ "yt-lockup-video" เราสามารถทำให้สิ่งนี้ง่ายขึ้นโดยแทนที่ด้วยสิ่งต่อไปนี้:
@FindBy(how=How.CSS,using="div[class='yt-lockup-tile yt-lockup-video']") private List<WebElement> videoElements;
นอกจากนี้ คุณสามารถใช้ @FindAll กับคำอธิบายประกอบ @FindBy หลายรายการเพื่อค้นหาองค์ประกอบที่ตรงกับตัวระบุตำแหน่งที่ระบุ:
@FindAll({@FindBy(how=How.ID, using=”username”), @FindBy(className=”username-field”)}) private WebElement user_name;
ตอนนี้เราสามารถแสดงหน้าเว็บเป็นคลาส Java และใช้ Page Factory เพื่อเริ่มต้นตัวแปร WebElement ได้อย่างง่ายดาย ถึงเวลาแล้วที่เราจะเห็นว่าเราจะเขียนการทดสอบ Selenium อย่างง่ายโดยใช้รูปแบบ Page Object และ Page Factory ได้อย่างไร
โครงการทดสอบระบบอัตโนมัติของซีลีเนียมอย่างง่ายใน Java
สำหรับบทช่วยสอนแบบจำลอง Page Object ของเรา เรามาทำให้นักพัฒนาลงชื่อสมัครใช้ Toptal โดยอัตโนมัติ ในการทำเช่นนั้น เราจำเป็นต้องทำให้ขั้นตอนต่อไปนี้เป็นไปโดยอัตโนมัติ:
เยี่ยมชม www.toptal.com
คลิกที่ปุ่ม “สมัครเป็นนักพัฒนา”
บนหน้าพอร์ทัลตรวจสอบก่อนว่าเปิดอยู่หรือไม่
คลิกที่ปุ่ม “เข้าร่วม Toptal”
กรอกแบบฟอร์ม
ส่งแบบฟอร์มโดยคลิกที่ปุ่ม “เข้าร่วม Toptal”
ตั้งโครงการ
ดาวน์โหลดและติดตั้ง Java JDK
ดาวน์โหลดและติดตั้ง InteliJ Idea
สร้างโปรเจ็กต์ Maven ใหม่
เชื่อมโยง “Project SDK” กับ JDK ของคุณ เช่น: บน Windows “C:\Program Files\Java\jdkxxx”
ตั้งค่า groupId และ artifactId:
<groupId>SeleniumTEST</groupId> <artifactId>Test</artifactId>
- เพิ่มการพึ่งพา Selenium และ JUnit Maven ในไฟล์ POM ของโปรเจ็กต์
<dependencies> <!-- JUnit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- Selenium --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-firefox-driver</artifactId> <version>${selenium.version}</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-support</artifactId> <version>${selenium.version}</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>${selenium.version}</version> </dependency> </dependencies>
แทนที่เวอร์ชัน Selenium และ JUnit Version ด้วยหมายเลขเวอร์ชันล่าสุดที่สามารถพบได้โดยการค้นหา JUnit Maven บน Google และบนไซต์ Selenium
ณ จุดนี้ หากเปิดใช้งานการสร้างอัตโนมัติ การขึ้นต่อกันควรเริ่มดาวน์โหลดโดยอัตโนมัติ ถ้าไม่เช่นนั้น ให้เปิดใช้งาน Plugins > install > install:install ใต้แผง Maven Projects ที่ด้านขวาของ IntelliJ Idea IDE ของคุณ
เมื่อโปรเจ็กต์ได้รับการบูทสแตรปแล้ว เราสามารถเริ่มสร้างแพ็คเกจการทดสอบภายใต้ “src/test/java” ตั้งชื่อแพ็คเกจว่า “com.toptal” และสร้างแพ็คเกจเพิ่มเติมอีกสองแพ็คเกจภายใต้: “com.toptal.webpages” และ “com.toptal.tests”

เราจะเก็บคลาส Page Object/Page Factory ไว้ภายใต้ “com.toptal.webpages” และทดสอบรูทีนภายใต้ “com.toptal.tests”
ตอนนี้ เราสามารถเริ่มสร้างคลาส Page Object ของเราได้แล้ว
วัตถุหน้าโฮมเพจ
สิ่งแรกที่เราต้องดำเนินการคือสำหรับหน้าแรกของ Toptal (www.toptal.com) สร้างชั้นเรียนภายใต้ "com.toptal.webpages" และตั้งชื่อเป็น "HomePage"
package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.How; import org.openqa.selenium.support.PageFactory; public class HomePage { private WebDriver driver; //Page URL private static String PAGE_URL="https://www.toptal.com"; //Locators //Apply as Developer Button @FindBy(how = How.LINK_TEXT, using = "APPLY AS A DEVELOPER") private WebElement developerApplyButton; //Constructor public HomePage(WebDriver driver){ this.driver=driver; driver.get(PAGE_URL); //Initialise Elements PageFactory.initElements(driver, this); } public void clickOnDeveloperApplyButton(){ developerApplyButton.click(); } }
การกำหนดตัวระบุตำแหน่งองค์ประกอบ
บนหน้าแรกของ Toptal เราสนใจองค์ประกอบหนึ่งโดยเฉพาะ นั่นคือปุ่ม "สมัครเป็นนักพัฒนา" เราสามารถค้นหาองค์ประกอบนี้ได้โดยจับคู่ข้อความซึ่งเป็นสิ่งที่เราทำด้านบน ในขณะที่สร้างแบบจำลองหน้าเว็บเป็นคลาส Page Object การค้นหาและระบุองค์ประกอบมักจะกลายเป็นงานที่น่าเบื่อ ด้วยเครื่องมือดีบั๊กของ Google Chrome หรือ Firefox สิ่งนี้ทำให้ง่ายขึ้น เมื่อคลิกขวาที่องค์ประกอบใดๆ บนหน้า คุณสามารถเปิดใช้งานตัวเลือก "ตรวจสอบองค์ประกอบ" จากเมนูบริบทเพื่อค้นหาข้อมูลโดยละเอียดเกี่ยวกับองค์ประกอบ
วิธีหนึ่งทั่วไป (และที่ฉันชอบ) คือการค้นหาองค์ประกอบโดยใช้ส่วนขยาย FireBug ของ Firefox ร่วมกับไดรเวอร์เว็บ Firefox ใน Selenium หลังจากติดตั้งและเปิดใช้งานส่วนขยาย FireBug คุณสามารถคลิกขวาที่หน้าและเลือก "ตรวจสอบองค์ประกอบด้วย FireBug" เพื่อเปิด FireBug จากแท็บ HTML ของ FireBug คุณสามารถคัดลอก XPath, CSS Path, ชื่อแท็ก หรือ "Id" (ถ้ามี) ขององค์ประกอบใดๆ ในหน้าได้
โดยการคัดลอก XPath ขององค์ประกอบในภาพหน้าจอด้านบน เราสามารถสร้างฟิลด์ WebElement สำหรับมันใน Page Object ของเราได้ดังนี้:
@FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1") WebElement heading;
หรือเพื่อให้ง่ายขึ้น เราสามารถใช้ชื่อแท็ก “h1” ที่นี่ ตราบใดที่ระบุองค์ประกอบที่เราสนใจได้โดยไม่ซ้ำกัน:
@FindBy(tagName = "h1") WebElement heading;
DeveloperPortalPage Page Object
ต่อไป เราจำเป็นต้องมี Page Object ที่แสดงถึงหน้าพอร์ทัลสำหรับนักพัฒนา ซึ่งเราสามารถเข้าถึงได้โดยคลิกที่ปุ่ม “Apply As A Developer”
ในหน้านี้ เรามีสององค์ประกอบที่น่าสนใจ เพื่อตรวจสอบว่าหน้าโหลดแล้วหรือไม่ เราต้องการตรวจสอบการมีอยู่ของส่วนหัว และเราต้องการฟิลด์ WebElement สำหรับปุ่ม "เข้าร่วม Toptal"
package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class DeveloperPortalPage { private WebDriver driver; @FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1") private WebElement heading; @FindBy(linkText = "JOIN TOPTAL") private WebElement joinToptalButton; //Constructor public DeveloperPortalPage (WebDriver driver){ this.driver=driver; //Initialise Elements PageFactory.initElements(driver, this); } //We will use this boolean for assertion. To check if page is opened public boolean isPageOpened(){ return heading.getText().toString().contains("Developer portal"); } public void clikOnJoin(){ joinToptalButton.click(); } }
DeveloperApplyPage Page Object
และสุดท้าย สำหรับออบเจ็กต์หน้าที่สามและหน้าสุดท้ายสำหรับโปรเจ็กต์นี้ เรากำหนดออบเจ็กต์ที่แสดงถึงหน้าที่มีแบบฟอร์มใบสมัครของนักพัฒนา เนื่องจากเราต้องจัดการกับฟิลด์ของฟอร์มจำนวนมากที่นี่ เราจึงกำหนดตัวแปร WebElement หนึ่งตัวสำหรับทุกฟิลด์ของฟอร์ม เราค้นหาแต่ละฟิลด์ด้วย "id" ของช่องนั้น และเรากำหนดวิธี setter พิเศษสำหรับทุกฟิลด์ที่จำลองการกดแป้นพิมพ์สำหรับฟิลด์ที่เกี่ยวข้อง
package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class DeveloperApplyPage { private WebDriver driver; @FindBy(tagName = "h1") WebElement heading; @FindBy() WebElement developer_email; @FindBy() WebElement developer_password; @FindBy() WebElement developer_password_confirmation; @FindBy() WebElement developer_full_name; @FindBy() WebElement developer_skype; @FindBy() WebElement join_toptal_button; //Constructor public DeveloperApplyPage(WebDriver driver){ this.driver=driver; //Initialise Elements PageFactory.initElements(driver, this); } public void setDeveloper_email(String email){ developer_email.clear(); developer_email.sendKeys(email); } public void setDeveloper_password(String password){ developer_password.clear(); developer_password.sendKeys(password); } public void setDeveloper_password_confirmation(String password_confirmation){ developer_password_confirmation.clear(); developer_password_confirmation.sendKeys(password_confirmation); } public void setDeveloper_full_name (String fullname){ developer_full_name.clear(); developer_full_name.sendKeys(fullname); } public void setDeveloper_skype (String skype){ developer_skype.clear(); developer_skype.sendKeys(skype); } public void clickOnJoin(){ join_toptal_button.click(); } public boolean isPageOpened(){ //Assertion return heading.getText().toString().contains("Apply to join our network as a developer"); } }
การเขียนแบบทดสอบซีลีเนียมอย่างง่าย
ด้วยคลาส Page Object ที่เป็นตัวแทนของเพจของเรา และการโต้ตอบของผู้ใช้เป็นเมธอดของพวกมัน ตอนนี้เราสามารถเขียนรูทีนการทดสอบอย่างง่ายของเราเป็นชุดของการเรียกเมธอดอย่างง่ายและการยืนยัน
package com.toptal.tests; import com.toptal.webpages.DeveloperApplyPage; import com.toptal.webpages.DeveloperPortalPage; import com.toptal.webpages.HomePage; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import java.net.URL; import java.util.concurrent.TimeUnit; public class ApplyAsDeveloperTest { WebDriver driver; @Before public void setup(){ //use FF Driver driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); } @Test public void applyAsDeveloper() { //Create object of HomePage Class HomePage home = new HomePage(driver); home.clickOnDeveloperApplyButton(); //Create object of DeveloperPortalPage DeveloperPortalPage devportal= new DeveloperPortalPage(driver); //Check if page is opened Assert.assertTrue(devportal.isPageOpened()); //Click on Join Toptal devportal.clikOnJoin(); //Create object of DeveloperApplyPage DeveloperApplyPage applyPage =new DeveloperApplyPage(driver); //Check if page is opened Assert.assertTrue(applyPage.isPageOpened()); //Fill up data applyPage.setDeveloper_email("[email protected]"); applyPage.setDeveloper_full_name("Dejan Zivanovic Automated Test"); applyPage.setDeveloper_password("password123"); applyPage.setDeveloper_password_confirmation("password123"); applyPage.setDeveloper_skype("automated_test_skype"); //Click on join //applyPage.clickOnJoin(); } @After public void close(){ driver.close(); } }
กำลังดำเนินการทดสอบ
ณ จุดนี้ โครงสร้างโครงการของคุณควรมีลักษณะดังนี้:
หากคุณต้องการเรียกใช้การทดสอบ ให้เลือก “ApplyAsDeveloperTest” จากแผนผัง คลิกขวาที่มันแล้วเลือก Run 'ApplyAsDeveloperTest'
เมื่อทำการทดสอบแล้ว คุณจะเห็นผลลัพธ์ที่มุมล่างซ้ายของ IDE ของคุณ:
บทสรุป
Page Object และ Page Factory ทำให้ง่ายต่อการสร้างแบบจำลองหน้าเว็บใน Selenium และทดสอบโดยอัตโนมัติ และทำให้ชีวิตของทั้ง Developer และ QA ง่ายขึ้นมาก เมื่อทำถูกต้องแล้ว คลาส Page Object เหล่านี้สามารถนำกลับมาใช้ใหม่ได้ในชุดทดสอบทั้งหมดของคุณ และให้โอกาสตัวคุณเองในการดำเนินการทดสอบ Selenium แบบอัตโนมัติสำหรับโปรเจ็กต์ของคุณตั้งแต่เนิ่นๆ โดยไม่กระทบต่อการพัฒนาที่คล่องตัว ด้วยการแยกการโต้ตอบของผู้ใช้ออกจากโมเดลออบเจ็กต์หน้าเว็บของคุณ และทำให้กิจวัตรการทดสอบของคุณเบาและเรียบง่าย คุณสามารถปรับชุดการทดสอบของคุณให้เข้ากับข้อกำหนดที่เปลี่ยนแปลงได้โดยใช้ความพยายามเพียงเล็กน้อย
ฉันหวังว่าฉันจะสามารถแสดงให้คุณเห็นถึงวิธีการเขียนโค้ดทดสอบที่ดีและสะอาดซึ่งดูแลรักษาง่าย ฉันจะจบบทความด้วยคำพูด QA ที่ฉันโปรดปราน:
คิดสองครั้ง รหัสครั้งเดียว!