Using the PageFactory class for exposing the elements on a page

To implement the Page Object model in tests, we need to create a Page Object class for each page that is being tested. For example, to test the BMI Calculator application, a BMI Calculator page class will be defined, which will expose the internals of the BMI Calculator page to the test, as shown in following diagram. This is done by using the PageFactory class of Selenium WebDriver API:

Using the PageFactory class for exposing the elements on a page

Getting ready

Before exposing the elements of a page, we need to do the following:

  • Identify the locators that will be needed to find all the required elements from a page uniquely
  • Define the structure of the package and classes for the page

How to do it...

Let's implement a Page Object test for the BMI Calculator page using the PageFactory class with the following steps:

  1. Define and create a package for all the page objects from the application for logical grouping. For example, seleniumcookbook.tests.pageobjects is created to define all the page objects.
  2. Create a new Java class file. Give the name of the page we will be testing from the application to this class. For example, we will be creating a page object for the BMI Calculator application, so the class name could be BmiCalcPage. This is a single page application. The Java class file will have the following code:
    package com.secookbook.examples.chapter08.pageobjects;
    
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.support.FindBy;
    import org.openqa.selenium.support.PageFactory;
    
    public class BmiCalcPage  {
    }
  3. Define elements from the BMI Calculator page as instance variables in the BmiCalcPage class created in step 1. Use the name or id attributes to name these variables, as follows:
    private WebElement heightCMS;
    private WebElement weightKg;
    private WebElement Calculate;
    private WebElement bmi;
    
    @FindBy(id = "bmi_category")
    private WebElement bmiCategory;
    
    private WebDriver driver;
  4. Add a constructor to the BmiCalcPage class, which will call the PageFactory.initElements() method to initialize the elements in the class. In other words, map the elements to the variables in the BmiCalcPage class, as follows:
    public BmiCalcPage(WebDriver driver) {
      this.driver = driver;
      PageFactory.initElements(driver, this);
    }
  5. We will add the accessor methods for Height, Weight, and Calculate button fields so we can set values or perform operation like clicking a button:
      public void setHeight(String height) {
        heightCMS.sendKeys(height);
      }
    
      public void setWeight(String weight) {
        weightKg.sendKeys(weight);
      }
    
      public void calculateBmi() {
        Calculate.click();
      }
  6. Also, add the accessor method to read the values from the Bmi and Bmi Category fields, as shown in the following code:
    public String getBmi() {
      return bmi.getAttribute("value");
    }
    
    public String getBmiCategory() {
      return bmiCategory.getAttribute("value");
    }
  7. Finally, create a test that will use the BmiCalcPage class to test the BMI Calculator page, as follows:
    package com.secookbook.examples.chapter08.tests;
    
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.chrome.ChromeDriver;
    
    import static org.junit.Assert.*;
    
    import com.secookbook.examples.chapter08.pageobjects.BmiCalcPage;
    
    public class BmiCalculatorTests {
    
      private WebDriver driver;
    
      @Before
      public void setUp() {
        driver = new ChromeDriver();
        driver.get("http://cookbook.seleniumacademy.com/bmicalculator.html");
      }
    
      @Test
      public void testBmiCalculation() {
        // Create an instance of Bmi Calculator Page class
        // and provide the driver
        CopyOfBmiCalcPage bmiCalcPage = new CopyOfBmiCalcPage(driver);
    
        // Set Height
        bmiCalcPage.setHeight("181");
    
        // Set Weight
        bmiCalcPage.setWeight("80");
    
        // Click on Calculate button
        bmiCalcPage.calculateBmi();
    
        // Verify Bmi & Bmi Category values
        assertEquals("24.4", bmiCalcPage.getBmi());
        assertEquals("Normal", bmiCalcPage.getBmiCategory());
      }
    
      @After
      public void tearDown() {
        driver.quit();
      }

How it works...

Using the Page Object model and the PageFactory class, the BMI Calculator page's elements are exposed through the BmiCalcPage class to the test instead of the test directly accessing the internals of the page.

When we initialize the page's object using the PageFactory class in the BmiCalcPage class, the PageFactory class searches for the elements on the page with the name or id attributes matching the name of the WebElement object declared in the BmiCalcPage class, as follows:

public BmiCalcPage(WebDriver driver) {
    PageFactory.initElements(driver, this);
}

The initElements() method takes the driver object created in the test and initializes the elements declared in the BmiCalcPage class. We can then directly call the methods on these elements, as follows:

// Set Height
bmiCalcPage.setHeight("181");

// Set Weight
bmiCalcPage.setWeight("80");

// Click on Calculate button
bmiCalcPage.calculateBmi();

The FindBy annotations

Finding elements using the name or id attributes may not always work and we might need to use advanced locator strategies such as XPath or CSS selectors. Using the FindBy annotation, we can locate the elements within the PageFactory class, as follows:

@FindBy(id = "heightCMS")
public WebElement heightField;

We declared a public member for the height element and used the @FindBy annotation, specifying the id as a locator for finding this element on the page.

The CacheLookUp attribute

One downside to using the @FindBy annotation is that every time we call a method on the WebElement object, the driver will go and find it on the current page again. This is useful in applications where elements are dynamically loaded, or AJAX-heavy applications.

However, in applications where we know that the element is always going to be there and stay the same without any change, it would be handy if we could cache the element once we find it. In order to do this, we use the @CacheLookUp annotation along with the @FindBy annotation, as follows:

@FindBy(id = "heightCMS")
@CacheLookup
public WebElement heightField;

This tells the PageFactory.initElements() method to cache the element once it's located. Tests work faster with cached elements when these elements are used repeatedly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.236.27