In the previous tutorial, we discussed a user-designed Page Object Model which we generally implement in test automation framework. We have seen that POM defines the Object repository which is completely independent of the test designs. This POM provides the environment of a centralized object repository system so that we can conveniently change the objects whenever a new UI design proposed by the business. We also discussed the major advantages of using the Page Object Model over any other patterns of writing automation tests. Today we are going to discuss the extended version of the Page Object Model which we call it Page Factory Model.
Page Factory Model is not user designed POM, rather, it has the direct implementation in Selenium openqa library. This is one of the most innovative techniques to manage the centralized page object repository. To begin with Page Factory Model, we need to import the following packages:
import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.How; import org.openqa.selenium.support.PageFactory;
What is Page Factory Model?
Page Factory Model is the Selenium’s inbuilt concept to design Page Object Model. It initializes web elements which are defined as Page objects. Web elements are defined by using @FindBy annotation and initialized by initElements() method.
@FindBy annotation accepts all the locators like id, name, xpath, linkText, partialLinkText, cssSelector and className as its attributes.
How to define web elements using @FindBy annotation in Page Factory Model?
Page Factory supports two ways to define web elements using @FindBy annotation. Let’s discuss each one of them.
Technique# 1
@FindBy(how = How.ID, using = "email") private WebElement usernameField;
Technique# 2
@FindBy(id = "pass") private WebElement passwordField; @FindBy(xpath = "//*[contains(@class,'fb_logo')]") private WebElement logo;
Above definition will work when you are dealing with the single element. There is a different definition for handling multiple elements or a list of web elements. Following definition helps you when you have to declare a list of web element.
@FindBy(className = "result") private List<WebElement> searchResult;
How to utilize Cache in Selenium for faster execution in Page Factory Model?
Cache is beneficial when you are working on the majority of the static part of the web page. WebDriver will first check the presence of same cached element then it will perform the further action, hence, it does not require to load cached element again and again. We use @CacheLookup annotation to implement the cache of static elements in our Selenium project.
What is @CacheLookup annotation in Page Factory Model?
When there is static DOM then we give instruction to WebDriver via Page Factory Model to keep the reference to the static objects, thus, when it loads the same page again then it does not have to load those cached object, it will directly perform the action. Here initElements() method play an important role to store the cached data. We will discuss more this method in the next section of this tutorial.
The @CacheLoopup technique will not work for AJAX based application as it is highly dynamic. So, to work on the AJAX-based application we use AJAXElementLocatorFactory.
How to implement @CacheLookup annotation in a project with Page Factory Model?
Following the statement of code helps you in the initialization of the @CacheLookup annotation. You first need to import the library then you need to implement @CacheLookup annotation in code.
import org.openqa.selenium.support.CacheLookup;
Implementation in code:
@FindBy(id = "myuser") @CacheLookup private WebElement myUserField;
How to initialize Page objects defined with @FindBy annotation using initElements() method?
Now we need to initialize all the elements defined by @FindBy annotation. We use PageFactory.initElements() method which takes the instance of WebDriver and returns the Page object.
Technique# 1: When you create an instance of the factory class in Test class
We use below technique when we simply create the instance of the Page factory class in the test class. The initialized code will look like-
FactoryClass factory = new FactoryClass(driver); PageFactory.initElements(driver, factory);
Or,
FactoryClass factory = PageFactory.initElements(driver, FactoryClass.class);
Technique# 2: As a constructor of the Factory class inside the factory class
In this technique, you create the parameterized constructor of the factory class then you call the method inside the constructor.
public FactoryClass(WebDriver driver){ this.driver = driver; PageFactory.initElements(driver, this); }
How to implement Page Factory Model in Selenium Test Automation Project?
We have seen all the facet of Page Factory Model. It’s time now to learn the implementation of the Page Factory Model. Let’s take a scenario first.
Scenario: Facebook invalid login validation
Description: This is the same scenario which we used to clarify the Page Object Model. Here user will open Facebook and validate homepage by identifying its logo then it will send negative test data to the username and password field. If the application fails to log in the user, then the test will succeed.
Page Factory Model Implementation
FBHomePageFactory.class
package Test; 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 FBHomePageFactory { WebDriver driver; public FBHomePageFactory(WebDriver driver){ this.driver = driver; PageFactory.initElements(driver, this); } @FindBy(how = How.ID, using = "email") public WebElement usernameField; @FindBy(id = "pass") public WebElement passwordField; @FindBy(xpath = "//*[contains(@class,'fb_logo')]") public WebElement logo; @FindBy(xpath = "//input[@id='u_0_2']") public WebElement btnLogin; @FindBy(xpath = "//*[@role='button'][text()='Recover Your Account']") public WebElement btnRecovery; }
Test Design with TestNG class
FactoryTestDesign.class
package Test; import org.testng.annotations.Test; import org.testng.annotations.BeforeMethod; import java.util.concurrent.TimeUnit; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.testng.Assert; import org.testng.annotations.AfterMethod; public class FactoryTestDesign { WebDriver driver; @Test public void homePageTesting() throws InterruptedException { FBHomePageFactory factory = new FBHomePageFactory(driver); boolean homePageCheck = factory.logo.isDisplayed(); Assert.assertTrue(homePageCheck, "Logo is present and User landed on home page"); Thread.sleep(3000); factory.usernameField.sendKeys("tester@testingdemo.com"); factory.passwordField.sendKeys("testing123"); factory.btnLogin.click(); Thread.sleep(3000); boolean recoveryPage = factory.btnRecovery.isDisplayed(); Assert.assertTrue(recoveryPage, "Negative testing success"); } @BeforeMethod public void setUp() { System.setProperty("webdriver.chrome.driver", "C:\\Selenium\\chromedriver.exe"); driver = new ChromeDriver(); driver.get("https://www.facebook.com/"); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); driver.manage().window().maximize(); } @AfterMethod public void tearDown() { driver.close(); driver.quit(); } }