Page Object Model (POM) & Page Factory i Selenium
Hvad er sideobjektmodel?
Sideobjektmodel (POM) er et designmønster, populært brugt i testautomatisering, der skaber Object Repository til web-UI-elementer. Fordelen ved modellen er, at den reducerer kodeduplikering og forbedrer testvedligeholdelse.
Under denne model skal der for hver webside i applikationen være en tilsvarende sideklasse. Denne sideklasse identificerer webelementerne på den pågældende webside og indeholder også sidemetoder, som udfører handlinger på disse webelementer. Navnet på disse metoder skal angives i henhold til den opgave, de udfører, dvs. hvis en indlæser venter på, at betalingsgatewayen vises, kan POM-metodenavnet være waitForPaymentScreenDisplay().
Hvorfor sideobjektmodel?
Start af en UI-automatisering i Selenium WebDriver er IKKE en svær opgave. Du skal bare finde elementer, udføre operationer på det.
Overvej dette enkle script til at logge ind på et websted
Som du kan se, er alt, hvad vi gør, at finde elementer og udfylde værdier for disse elementer.
Dette er et lille script. Scriptvedligeholdelse ser let ud. Men med tiden vil testsuiten vokse. Efterhånden som du tilføjer flere og flere linjer til din kode, bliver tingene svære.
Det største problem med scriptvedligeholdelse er, at hvis 10 forskellige scripts bruger det samme sideelement, med enhver ændring i det element, skal du ændre alle 10 scripts. Dette er tidskrævende og fejludsat.
En bedre tilgang til scriptvedligeholdelse er at oprette en separat klassefil, som kan finde webelementer, udfylde dem eller verificere dem. Denne klasse kan genbruges i alle scripts ved hjælp af dette element. Hvis der fremover er en ændring i webelementet, skal vi lave ændringen i kun 1 klassefil og ikke 10 forskellige scripts.
Denne tilgang kaldes Page Object Model i Selenium. Det hjælper med at gøre koden mere læsbar, vedligeholdelsesvenlig og genbrugelig.
Fordele ved POM
- Page Object Design Pattern siger, at operationer og flows i brugergrænsefladen skal adskilles fra verifikation. Dette koncept gør vores kode renere og nem at forstå.
- Den anden fordel er, at objektlageret er uafhængigt af testcases, så vi kan bruge det samme objektlager til et andet formål med forskellige værktøjer. For eksempel kan vi integrere Page Object Model i Selenium med TestNG/JUnit til funktionel Test og samtidig med JBehave/Cucumber til accepttest.
- Koden bliver mindre og optimeret på grund af de genanvendelige sidemetoder i POM-klasserne.
- Metoder får mere realistiske navne, som nemt kan kortlægges med operationen, der sker i brugergrænsefladen. dvs. hvis vi lander på startsiden efter at have klikket på knappen, vil metodenavnet være som 'gotoHomePage()'.
Hvordan implementerer man POM?
Simpel POM:
Det er den grundlæggende struktur af sideobjektmodelramme, hvor alle webelementer i AUT og metoden, der fungerer på disse webelementer, vedligeholdes inde i en klassefil. En opgave som verifikation bør være adskilt som en del af testmetoder.
Komplet eksempel
test sag: Gå til Guru99 Demo Site.
Trin 1) Gå til Guru99 Demo Site
Trin 2) Tjek teksten "Guru99 Bank" på hjemmesiden
Trin 3) Log ind på applikationen
Trin 4) Bekræft, at hjemmesiden indeholder tekst som "Manger Id: demo"
Her har vi at gøre med 2 sider
- Login-side
- Hjemmeside (vises, når du logger ind)
Derfor opretter vi 2 POM i Selenium klasser
Guru99 Login side 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 Hjemmeside POM in 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 Simpel POM ind Selenium Test tilfælde
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")); }
Hvad er Page Factory i Selenium?
Page Factory i Selenium er et indbygget Page Object Model-rammekoncept til Selenium WebDriver, men den er meget optimeret. Det bruges til initialisering af sideobjekter eller til at instansiere selve sideobjektet. Det bruges også til at initialisere sideklasseelementer uden at bruge "FindElement/s."
Også her følger vi konceptet om adskillelse af Page Object Repository og testmetoder. Derudover med hjælp fra klassen PageFactory i Selenium, bruger vi anmærkninger @FindBy for at finde WebElement. Vi bruger initElements-metoden til at initialisere webelementer
@FindBy kan acceptere tagName, partialLinkText, name, linkText, id, css, className, xpath som attributter.
Lad os se på det samme eksempel som ovenfor ved at bruge Page Factory
Guru99 Login-side med 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 Hjemmeside med 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 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 med Page Factory koncept
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")); } }
Komplet projektstruktur vil se ud som diagrammet:
AjaxElementLocatorFactory
AjaxElementLocatorFactory er et dovent indlæsningskoncept af PageFactory i Selenium. Det bruges kun til at finde webelementerne, når elementerne bruges i enhver operation. Den tildeler en timeout for WebElements til objektsideklassen. En af de vigtigste fordele ved at bruge mønsteret PageFactory i Selenium er AjaxElementLocatorFactory Class.
Her, når en operation udføres på et element, starter ventetiden på dets synlighed kun fra det øjeblik. Hvis elementet ikke findes i det givne tidsinterval, Test sag udførelse vil kaste 'NoSuchElementException' undtagelse.
Resumé
- Sideobjektmodel i Selenium WebDriver er et Object Repository-designmønster.
- Selenium sideobjektmodel skaber vores testkode, der kan vedligeholdes, genbruges.
- Page Factory er en optimeret måde at oprette objektlager i Page Object Model framework-konceptet.
- AjaxElementLocatorFactory er et lazy load-koncept i Page Factory – sideobjektdesignmønster til kun at identificere WebElements, når de bruges i enhver operation.
Download Selenium Projektfiler til demoen i denne vejledning