Page Object Model (POM) vs Page Factory in Selenium Automation

When building an automation testing framework, one of the crucial aspects is to determine how to design tests in a way that allows for the distribution of execution logic independently from the implementation.

As projects rapidly grow with new functionalities and involve changes in existing features, QA automation engineers face the following tasks:

  • Creating high-quality new tests
  • Supporting and refactoring existing tests with minimal changes in code and structure

To accomplish these tasks, we use the Page Object Model (POM).

In this article, we’ll talk about the significance of the Page Object Model pattern in Selenium automation. The article explores various methods of pattern implementation, including the standard approach and utilizing the Page Factory class. It delves into the main advantages of POM and provides a comparative analysis between POM and Page Factory.

What is Page Object Model (POM)?

Page Object Model (POM) is a design pattern that has gained popularity in automation testing for enhancing test maintenance and reducing code duplication. The main specifications of this pattern are:

  • Creation of a separate class in the project that matches the appropriate web page of the application
  • The created page class includes a declaration of the corresponding web elements located on this page, along with a list of methods that interact with these elements

As additional useful possibilities for enhancing the pattern, the following can be defined:

  • Creation of a parent class that includes all the common content for page classes. It is good practice to define such a class as abstract to avoid the possibility of creating objects of non-existent web pages in the application
  • Preparing a separate class for actions with elements — such as entering data, clicking on elements, and scrolling to elements

To better understand the effectiveness of the POM approach, let’s take a look at an example of a typical automated test without using the Page Object Model.

Here’s a code snippet without POM (simple login test):

public class LoginWithOutPOMTest {
   WebDriver webDriver;

   @Before
   public void setUp() {
       try{
           WebDriverManager.chromedriver().setup();
           webDriver = new ChromeDriver();

           webDriver.manage().window().maximize();
           webDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(20));

       }catch (Exception e){
           Assert.fail("Can not create driver session");
       }
   }

   @Test
   public void validLogin() {
       webDriver.get("https://www.saucedemo.com");

       webDriver.findElement(By.id("user-name")).clear();
       webDriver.findElement(By.id("user-name")).sendKeys("standard_user");

       webDriver.findElement(By.id("password")).clear();
       webDriver.findElement(By.id("password")).sendKeys("secret_sauce");

       webDriver.findElement(By.id("login-button")).click();

       String text = webDriver.findElement(
               By.cssSelector("div.header_secondary_container span.title")).getText();
       Assert.assertTrue("Login was not successful", text.contains("Products"));
   }

   @After
   public void tearDown() {
       webDriver.quit();
   }

}

The above approach has several disadvantages, including:

  • The web driver has been initialized directly in the test class
  • Lack of a clean separation between the test code and page-specific code (locators)
  • With more complex tests, the number of required elements and actions will significantly increase, leading to code readability complications
  • If the web elements are used in multiple tests, they must be declared separately for each individual test
  • If element locators are changed, updates must be applied to all places in the project where these elements are used

Page Object Model implementation

Now, let’s apply the POM pattern to the test described above and observe the difference in implementation. The POM implementation will include the following steps:

  • Creation of separate classes which match web pages of the application (in our case there will be 2 classes — LoginPage and ProductsPage)
  • Declaration of web elements in both page classes using By (abstract class in Selenium)
  • Preparing methods for interaction with elements in each class
  • Creation of parent class for tests

Take a look at Sample Project Structure with POM:

Page Object Model (POM) vs Page Factory in Selenium Automation

The LoginPage class defines web elements present on the Login page, methods for interacting with them, the webdriver object, and a constructor.

public class LoginPage {

   WebDriver webDriver;

   /**
    * create page constructor
    */
   public LoginPage(WebDriver webDriver) {
       this.webDriver = webDriver;
   }

   /**
    * define web elements for Login page
    */
   By userNameField = By.id("user-name");
   By passWordField = By.id("password");
   By loginBtn = By.id("login-button");


   // method for entering username
   public void enterUserName(String userName) {
       webDriver.findElement(userNameField).clear();
       webDriver.findElement(userNameField).sendKeys(userName);
   }

   // method for entering password
   public void enterPassWord(String passWord) {
       webDriver.findElement(passWordField).clear();
       webDriver.findElement(passWordField).sendKeys(passWord);
   }

   // method for clicking on Login button
   public void clickOnLogin() {
       webDriver.findElement(loginBtn).click();
   }

   // method for opening login page
   public void openLoginPage() {
       try{
           webDriver.get("https://www.saucedemo.com/");
       }catch (Exception e){
           Assert.fail("Impossible to open Login page");
       }
   }

}

The ProductsPage class defines the web elements present on the Products page, along with methods for interacting with them, the webdriver object, and the constructor.

public class ProductsPage {

   WebDriver webDriver;

   /**
    * create page constructor
    */
   public ProductsPage(WebDriver webDriver) {
       this.webDriver = webDriver;
   }

   /**
    * define web elements for Products page
    */
   By productsTitle = By.cssSelector("div.header_secondary_container span.title");

   public void checkProductsPageOpened() {
       String title = webDriver.findElement(productsTitle).getText();
       Assert.assertTrue("Products page was not opened", title.contains("Products"));
   }

}

ParentTest is the parent class for test classes. This class provides the following actions:

  • Preparing methods for a driver session creating (before each test beginning) and closing it (after the test run is finished)
  • Creation and initialization page classes objects
public class ParentTest {

   WebDriver webDriver;
   protected LoginPage loginPage;
   protected ProductsPage productsPage;

   @Before
   public void setUp() {
       try{
           WebDriverManager.chromedriver().setup();
           webDriver = new ChromeDriver();

           webDriver.manage().window().maximize();
           webDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(20));

           loginPage = new LoginPage(webDriver);
           productsPage = new ProductsPage(webDriver);
       }catch (Exception e){
           Assert.fail("Can not create driver session");
       }
   }

   @After
   public void tearDown() {
       webDriver.quit();
   }

}

The Test Class describes only the logic of test execution (without driver initialization, finding web elements, and actions with them).

public class LoginWithPOMTest extends ParentTest {

   String username = "standard_user";
   String password = "secret_sauce";

   @Test
   public void validLogin() {
       loginPage.openLoginPage();
       loginPage.enterUserName(username);
       loginPage.enterPassWord(password);
       loginPage.clickOnLogin();

       productsPage.checkProductsPageOpened();
   }

}

Advantages of Using POM

After comparing test implementations with POM and without POM, we can identify the following POM advantages:

  • Code reusability. We can use variables and methods of the page class in all tests without redefining the web elements
  • Easy test maintenance and refactoring
  • Code readability. This is achieved by separating the page code from the test code

What is Page Factory?

Another variant for the Page Object Model implementation is using the Page Factory class provided by Selenium WebDriver. The main specifications of the Page Factory are:

  • Usage of the @FindBy annotation to locate and declare elements (with different locator strategies)
  • Usage of the static method initElements() to initialize elements of the current webpage which were declared with the @FindBy annotation
  • Supporting the lazy initialization concept (using AjaxElementLocatorFactory class) for identifying web elements only when they are used in any operations and actions

Page Factory Implementation

As a basis, we will take the previously created project and update it using Page Factory. The project structure, Test, and Parent classes will remain unmodified. The changes will only be made in the Page classes.

The following changes were made:

  • Declared web elements using the @FindBy annotation and WebElement interface
  • Initialized web elements in the class constructor using the method initElements() of the PageFactory class
  • Updated methods that interact with elements

For LoginPage:

public class LoginPage {

   WebDriver webDriver;

   public LoginPage(WebDriver webDriver) {
       this.webDriver = webDriver;
       PageFactory.initElements(webDriver, this);
   }

   /**
    * define web elements for Login page using @FindBy annotation
    */
   @FindBy(id = "user-name")
   WebElement userNameField;

   @FindBy(id = "password")
   WebElement passWordField;

   @FindBy(id = "login-button")
   WebElement loginBtn;


   // method for entering username
   public void enterUserName(String userName) {
       userNameField.clear();
       userNameField.sendKeys(userName);
   }


   // method for entering password
   public void enterPassWord(String passWord) {
       passWordField.clear();
       passWordField.sendKeys(passWord);
   }

   // method for clicking on Login button
   public void clickOnLogin() {
       loginBtn.click();
   }

   // method for opening login page
   public void openLoginPage() {
       try {
           webDriver.get("https://www.saucedemo.com/");
       } catch (Exception e) {
           Assert.fail("Impossible to open Login page");
       }
   }



For ProductsPage:

public class ProductsPage {

   WebDriver webDriver;

   /**
    * create page constructor
    */
   public ProductsPage(WebDriver webDriver) {
       this.webDriver = webDriver;
       PageFactory.initElements(webDriver, this);
   }

   /**
    * define web elements for Products page using @FindBy annotation
    */
   @FindBy(css = "div.header_secondary_container span.title")
   WebElement productsTitle;

   public void checkProductsPageOpened() {
       String title = productsTitle.getText();
       Assert.assertTrue("Products page was not opened", title.contains("Products"));
   }

}

Page Object Model vs Page Factory

Let’s summarize and define the main differences between POM and Page Factory:

POM
PAGE FACTORY
POM

It is a design pattern

PAGE FACTORY

It is a class provided by Selenium WebDriver for POM implementation

POM

For finding web elements, the By class is used

PAGE FACTORY

For finding web elements, the @FindBy annotation is used

POM

Each page class object must be initialized individually

PAGE FACTORY

All page elements are initialized using the static method initElements()

POM

Does not provide lazy initialization

PAGE FACTORY

Provides lazy initialization

In Conclusion

Before setting up a framework for automation testing, one needs to consider the effective way of separating test business logic from the code for its implementation. This approach makes tests more readable, reusable, and easier to maintain.

To achieve these goals, you can utilize the Page Object Model (POM). This pattern can be implemented in a standard manner or via the Page Factory class.

In my opinion, POM implementation using Page Factory has more advantages compared to typical POM. Some of these advantages include the possibility to initialize all web elements at once, support for the lazy load concept, and more accurate element declaration using the @FindBy annotation.

See how we helped Kazidomi speed up feature delivery with test automation and become #1 startup in Belgium

Please enter your business email