In this tutorial, we will learn how to use the Page Object Model and Page Factory in Selenium Webdriver with simple examples so that you can understand the things easily and implement them in your project works.
What is the Page Object Model (POM) in Selenium?
Page Object Model (POM) is a Design Pattern for automating web applications and it has become popular because it let us allow to create an Object Repository for Web Elements that translates into enhancing test maintenance and reducing code duplication.
As per the approach of this model, we have to create a Page Class for each web page in the application. This Page class is further used to identify the Web Elements of that particular page and also contains corresponding methods to perform required operations on those Web Elements.
Also, following the best practices, the name of these methods should be given according to the functionality of those web elements. For example, if there is a login button, the corresponding method could be like clickLoginButton() and such method names are self-explanatory.
The following is an example of a sample Page Object Model approach. Here I have created a package with the name “apppages” that contains all page classes of the application and a separate “tests” package that will contain all the tests for automating the web application. The project structure of the POM project looks like the following image.
Why Do We Need a Page Object Model In Selenium?
Automating a web application in Selenium WebDriver is quite an easier thing. We simply find elements and perform required operations on them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; public class TestWithoutPOM { WebDriver driver; String browserDriverPath= "C:\\SeleniumBrowserDrivers\\chromedriver.exe"; @BeforeTest public void setup(){ System.setProperty("webdriver.chrome.driver",browserDriverPath); driver = new ChromeDriver(); driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS); driver.get("http://www.advantageonlineshopping.com/"); } @Test public void verifyLoggedInUser() throws InterruptedException { String landingPageTitle = driver.getTitle(); // Verifying Landing Home Page title Assert.assertTrue(landingPageTitle.contains("Advantage Shopping")); // Clicking on Login icon menu driver.findElement(By.id("hrefUserIcon")).click(); // Switching to login popup window driver.switchTo().activeElement(); // Set the value of user name driver.findElement(By.name("username")).sendKeys("mydemoaccount"); // Set the value of password and press ENTER Key driver.findElement(By.name("password")).sendKeys("Demo@123456"+Keys.ENTER); // Switching back to default window that is main Page driver.switchTo().defaultContent(); // Defining Explicit Wait WebDriverWait wait = new WebDriverWait(driver,5); // Waiting until the logged in user's name becomes visible wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id=\"menuUserLink\"]/span"))); String userName = driver.findElement(By.xpath("//*[@id=\"menuUserLink\"]/span")).getText(); System.out.println("Logged in user's name is:"+userName); // Verify Logged in User's name Assert.assertTrue(userName.contains("mydemoaccount")); } } |
The above script is quite simple. We are locating the desired elements and performing the required operations on them. If any of the project properties change in the future we can easily maintain this script. So far it looks good and there is no maintenance issue.
But in a real automation project, we have to write hundreds or thousands of automation scripts. You can just imagine if we follow the above simple approach and a few of the object properties got changed in the future due to any update in the application and those objects are being used in hundreds of tests, you will have to maintain on all impacted tests. In that case, maintenance will require humungous efforts. Also, it will be a very time-consuming task.
So how to tackle this problem. The better approach is to create a separate class file for each page that would find web elements on the page and perform all required operations on them like setting the value in a textbox and clicking a button and etc. This page class can be reused in all the scripts that are using their web elements.
Now in the future, if there is a change in the property of the web element, we just need to make the change in just one class file and not in hundreds of impacted scripts.
That is why this approach is called Page Object Model in Selenium. This approach makes the code more readable, maintainable, reusable, and scalable.
Advantages of Page Object Model in Selenium
- Maintainable Code – The major benefit of the Page Object Model is that if the GUI changes for any of the pages, the tests themselves don’t need to change, only the code within the page object needs to change. This makes automation scripts easier to maintain and highly optimized.
- Readable Code – Automation Test Scripts and Page-specific code are cleanly separated from each other. The code becomes less and more readable.
- Reusable Code – If multiple test scripts have to use the same web elements, there is no need to write code to handle the web element in the individual test scripts. Having a separate page class makes it reusable by making it accessible by any test script.
- Logical Method Name – The approach enforces us to give self-explanatory method names for page object elements that can be easily mapped with the operation to be performed on the GUI. i.e. if a have to click on a Login button, the method name could be clickOnLoginButton().
How to Implement the Page Object Model In Selenium?
In the section Why Do We Need a Page Object Model In Selenium? we have seen the simple automation script using without page object model structure.
In this section, we will automate the same test using the Page Object model.
Define Page Classes in POM in Selenium
In order to follow the Page Object Model, we will store the web elements present on each page in the corresponding java class. In this example, we will be interacting with two web pages that are Home Page and the Login page.
Home Page objects:
The Menu bar of the Home Page of the Advantage Shopping app looks like the following image. In this example, we will interact with two objects on the home page as highlighted in the below image.
Note: Logged in user’s name object will be visible only after successful login
We will define only the above two objects in the page class of the landing home page as shown below. We will also define the corresponding methods that can be performed on the page object methods in the page class.
The code for the LandingHomePage.java class is as follows where the page objects have been defined along with the methods to perform the desired action on the page objects.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
package apppages; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; public class LandingHomePage { WebDriver driver; By userLoginMenuIcon = By.id("menuUser"); By loggedInUserName = By.xpath("//*[@id=\"menuUserLink\"]/span"); // This Constructor will be automatically called when the object of the class is created public LandingHomePage(WebDriver driver) { this.driver = driver; } // Get the title of Landing Home Page public String getLandingHomePageTitle() { return driver.getTitle(); } // Click on User login Menu Button public void clickLogin() { driver.findElement(userLoginMenuIcon).click(); } // Get Login User Name public String getLoginUserName() { return driver.findElement(loggedInUserName).getText(); } public void waitForloggedInUserNameToBecomeVisible() { // Defining Explicit Wait WebDriverWait wait = new WebDriverWait(driver,5); // Waiting until the logged in user's name becomes visible wait.until(ExpectedConditions.visibilityOfElementLocated(loggedInUserName)); } } |
Login Page Objects:
The login popup page looks like the following image.
On the login Popup page, we will interact with only two objects that are user name and password. Instead of clicking on the Sign-in button we will press Enter key.
The code for the LoginPage.java class is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
package apppages; import org.openqa.selenium.By; import org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver; public class LoginPage { WebDriver driver; By userName = By.name("username"); By userPassword = By.name("password"); // This Constructor will be automatically called when the object of the class is created public LoginPage(WebDriver driver) { this.driver = driver; } // Set the value of user name in user name TextBox public void setUserName(String strUserName){ driver.findElement(userName).sendKeys(strUserName); } // Set the value of password in password TextBox public void setPassword(String strPassword){ driver.findElement(userPassword).sendKeys(strPassword+Keys.ENTER); } /** * This POM method will be used in actual test case to login to the application * @param strUserName * @param strPasword * @return */ public void login(String strUserName,String strPasword){ // Switching to Login Popup window driver.switchTo().activeElement(); //Enter user name this.setUserName(strUserName); //Enter password this.setPassword(strPasword); driver.switchTo().defaultContent(); } } |
How to Use Page Objects and Their Methods in Automation Test Classes?
The following VerifyLoggedInUser.java class uses both page object classes. In this class, I have created a POM-based script having two tests to give you a better idea of how we can implement page classes in multiple tests.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
package tests; import java.util.concurrent.TimeUnit; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import apppages.LandingHomePage; import apppages.LoginPage; public class VerifyLoggedInUser { WebDriver driver; // Creating object of Landing home page LandingHomePage objLandingHomePage; // Creating object of Login page LoginPage objLoginPage; String browserDriverPath= "C:\\SeleniumBrowserDrivers\\chromedriver.exe"; @BeforeTest public void setup(){ System.setProperty("webdriver.chrome.driver",browserDriverPath); driver = new ChromeDriver(); driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS); driver.get("http://www.advantageonlineshopping.com/#/"); } /** * This test case will login in advantageonlineshopping.com and verify * its title.It Should be Advantage Shopping * Login to the application * */ @Test public void loggedInToApplication_TC1(){ // Initializing Landing Home Page object objLandingHomePage = new LandingHomePage(driver); // Getting Landing Page title String landingPageTitle = objLandingHomePage.getLandingHomePageTitle(); // Verifying Landing Home Page title Assert.assertTrue(landingPageTitle.contains("Advantage Shopping")); objLandingHomePage.clickLoginMenuButton(); // Initializing Login Page Object objLoginPage = new LoginPage(driver); // Login to the Application objLoginPage.login("mydemoaccount", "Demo@123456"); } /** * This test case will login verify logged in user's name **/ @Test public void verifyLoggedInUser_TC2(){ // Waiting until logged in user's name becomes visible properly on the home page // objLandingHomePage has already been initialized in above test.No need to initialize again objLandingHomePage.waitForloggedInUserNameToBecomeVisible(); // Getting logged in user's name String userName = objLandingHomePage.getLoginUserName(); System.out.println("Logged in user's name is:"+userName); // Verifying Logged in User name Assert.assertTrue(userName.contains("mydemoaccount")); } } |
OutPut
If you run the tests the output will be as follows.
What is Page Factory in Selenium & How to Implement it?
While understanding the Page Object Model we saw that it is a Design Pattern for automating web applications and it allows to create an Object Repository for Web Elements that translates into enhancing test maintenance and reducing code duplication.
Having said that, Page Factory in Selenium is an inbuilt class that supports the Page Object Model out of the box and way more optimized than a simple Page Object Model. In the Page Factory model, @FindBy annotation is used to find the elements, and the initElements() method is used to load the elements.
The following is an example of declaring an element using @FindBy annotation in Page Factory.
1 2 3 4 5 6 |
// Element declaration in single line @FindBy(id="elementID") WebElement element1; // OR.. you can also do it in the following way @FindBy(id="elementID") WebElement element1; |
Note: We can use any of the locators in @FindBy annotation.
We will have to import “org.openqa.selenium.support.FindBy;” package to use @FindBy annotation.
The project structure of the Page Object Model and Page Factory will be the same as shown below and you don’t need to do any changes in the actual automation tests.
I have simply made a replica of project “POMExampleSelenium” to create “PageFactoryUsingPOMInSelenium” project and have done changes only on the page classes i.e LandingHomePage.java and LoginPage.java.
LandingHomePage with Page Factory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
package apppagesfactory; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class LandingHomePage { WebDriver driver; @FindBy(id="menuUser") WebElement userLoginMenuIcon; @FindBy(xpath="//*[@id=\"menuUserLink\"]/span") WebElement loggedInUserName; // This Constructor will be automatically called when the object of the class is created public LandingHomePage(WebDriver driver) { this.driver = driver; // The initElements method will initiate all WebElements PageFactory.initElements(driver, this); } // Get the title of Landing Home Page public String getLandingHomePageTitle() { return driver.getTitle(); } // Click on User login Menu Button public void clickLoginMenuButton() { userLoginMenuIcon.click(); } // Get Login User Name public String getLoginUserName() { return loggedInUserName.getText(); } public void waitForloggedInUserNameToBecomeVisible() { // Defining Explicit Wait WebDriverWait wait = new WebDriverWait(driver,5); // Waiting until the logged in user's name becomes visible wait.until(ExpectedConditions.visibilityOf(loggedInUserName)); } } |
Login Page with Page Factory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
package apppagesfactory; import org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class LoginPage { WebDriver driver; @FindBy(name="username") WebElement userName; @FindBy(name="password") WebElement userPassword; // This Constructor will be automatically called when the object of the class is created public LoginPage(WebDriver driver) { this.driver = driver; // The initElements method will initiate all WebElements PageFactory.initElements(driver, this); } // Set the value of user name in user name TextBox public void setUserName(String strUserName){ userName.sendKeys(strUserName); } // Set the value of password in password TextBox public void setPassword(String strPassword){ userPassword.sendKeys(strPassword+Keys.ENTER); } /** * This POM method will be used in actual test case to login to the application * @param strUserName * @param strPasword * @return */ public void login(String strUserName,String strPasword){ // Switching to Login Popup window driver.switchTo().activeElement(); //Enter user name this.setUserName(strUserName); //Enter password this.setPassword(strPasword); driver.switchTo().defaultContent(); } } |
VerifyLoggedInUser Test with Page Factory concept
This logical code of this class is the same as the Test class used in the Page Object Model. The only difference is the page class package name which I have changed here to apppagesfactory.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
package tests; import java.util.concurrent.TimeUnit; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import apppagesfactory.LandingHomePage; import apppagesfactory.LoginPage; public class VerifyLoggedInUserUsingPgFactory { WebDriver driver; // Creating object of Landing home page LandingHomePage objLandingHomePage; // Creating object of Login page LoginPage objLoginPage; String browserDriverPath= "C:\\SeleniumBrowserDrivers\\chromedriver.exe"; @BeforeTest public void setup(){ System.setProperty("webdriver.chrome.driver",browserDriverPath); driver = new ChromeDriver(); driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS); driver.get("http://www.advantageonlineshopping.com/#/"); } /** * This test case will login in advantageonlineshopping.com and verify * its title.It Should be Advantage Shopping * Login to the application * */ @Test public void loggedInToApplication_TC1(){ // Initializing Landing Home Page object objLandingHomePage = new LandingHomePage(driver); // Getting Landing Page title String landingPageTitle = objLandingHomePage.getLandingHomePageTitle(); // Verifying Landing Home Page title Assert.assertTrue(landingPageTitle.contains("Advantage Shopping")); objLandingHomePage.clickLoginMenuButton(); // Initializing Login Page Object objLoginPage = new LoginPage(driver); // Login to the Application objLoginPage.login("mydemoaccount", "Demo@123456"); } /** * This test case will login verify logged in user's name **/ @Test public void verifyLoggedInUser_TC2(){ // Waiting until logged in user's name becomes visible properly on the home page // objLandingHomePage has already been initialized in above test.No need to initialize again objLandingHomePage.waitForloggedInUserNameToBecomeVisible(); // Getting logged in user's name String userName = objLandingHomePage.getLoginUserName(); System.out.println("Logged in user's name is:"+userName); // Verifying Logged in User name Assert.assertTrue(userName.contains("mydemoaccount")); } } |
Test Result
AjaxElementLocatorFactory in Factory Class
AjaxElementLocatorFactory is a lazy load concept in Page Factory. It is used to identify web elements only when they are used in any operation. We can assign the timeout of a web element to the object class with the help of the AjaxElementLocatorFactory.The syntax is as follows.
1 2 3 4 5 6 |
AjaxElementLocatorFactory factory = new AjaxElementLocatorFactory(driver,20); PageFactory.initElements(factory, this); // OR PageFactory.initElements(new AjaxElementLocatorFactory(driver, 20), this); |
Conclusion
In this article, we have seen how to use the Page Object Model and Page Factory in Selenium to make the automation code maintainable, reusable, readable, and highly optimizable. Hope you have liked this article.
Recommended Posts
- How to Minimize Browser in Selenium WebDriver
- How To Resize Browser In Selenium WebDriver
- How to Scroll Down or UP a Page in Selenium Webdriver
- 4 Ways to Refresh Page In Selenium Webdriver
- Get ToolTip Text in Selenium WebDriver
- Wait Commands in Selenium WebDriver | Implicit | Explicit | Fluent Wait
- Send Keyboard Keys in Selenium Webdriver With Example
- Intuitive Way of MySQL Database Testing in Selenium | LeanFT