Web Automation ( Java and Selenium): Using LoadableComponent to Make Page Object more concise

Introduction

This article explains a nice way to make Page Object more meaningful and to reduce code duplication to validate the page load behaviour.
I would recommend first reading about the Page Object Model in Test automation if you are not familiar with it.

How most of the engineers write Page Object

Assume there is a Login Page for an application and Page Object is implemented for the same as below:

public class LoginPage {
  private final WebDriver driver;

    public LoginPage(WebDriver driver) {
    this.driver = driver;
  }

    public LoginPage navigate() {
    driver.get("https://example.com/login");
    return this;
  }

    public LoginPage enterUsername(String username) {
    driver.findElement(By.name("username")).sendKeys(username);
    return this;
  }

    public LoginPage enterPassword(String password) {
    driver.findElement(By.name("password")).sendKeys(password);
    return this;
  }
}

What is the problem?

In the above code, how do you ensure thatnavigate()redirected to the correct page?

  • What if it redirects to a 404 page?

  • What if it redirects to the wrong page?

One way to validate is to, check if the URL is correct or check if an element from the login page is present. In this case, we can check if the username field is present.

Our updated code would look like this:

// adding the extra method to previous code 
public boolean isLoaded() {
    return driver.getCurrentUrl().equals("https://example.com/login");
  }

// Another way
public boolean isLoaded() {
    return driver.findElement(userNameInput).isDisplayed();
  }

// Test Class
@Test
void checkLoginWithValidCredentials(){
    // We can use method chaining because we are returning class 
    // instance for navigate()    
    boolean isLoaded = new LoginPage(driver)
                .navigate()
                .isLoaded(); 
    Assert.assertTrue(isLoaded);
}

Looks good right? Of course, it does. But

  • Is page load our test objective?

  • Should we explicitly test if a page is loaded, for the test where we are aiming to test valid credentials?

  • What if we have multiple tests, this page-load logic and assertion be duplicated

  • In fact, if the pre-requisite of the test is, to have the login page to be loaded, then why to even proceed with the test if this pre-requisite is failed?

Solution?

Use LoadableComponent from Selenium. The official documentation suggests it to use it to make the page object more cleaner and that will fail fast and fail with information.

Let's refactor our code to use LoadableComponent

public class LoginPage extends LoadableComponent<LoginPage> {
  private final WebDriver driver;

    public LoginPage(WebDriver driver) {
    this.driver = driver;
  }

    // We need to override below two methods
    @Override
    protected void load() {
    driver.get(loginUrl);
  }

    @Override
    protected void isLoaded() throws Error {
    String currentUrl = driver.getCurrentUrl();
    if (!currentUrl.equals(loginUrl)) {
      Assert.assertEquals(currentUrl, loginUrl, "Login Page is not loaded");
    }
  }

    public LoginPage enterUsername(String username) {
    driver.findElement(By.name("username")).sendKeys(username);
    return this;
  }

    public LoginPage enterPassword(String password) {
    driver.findElement(By.name("password")).sendKeys(password);
    return this;
  }
}

// Test Class
@Test
void checkLoginWithValidCredentials(){
    new LoginPage(driver).get()
            .enterUsername()
            .enterPassword();
}

Benefits?

  • Less code in the Test method and tests look cleaner

  • Page load is asserted even before the test tries to interact with the element

  • If the right page is not loaded, it will fail fast with an assertion error.

  • By extending LoadableComponent , we ensure consistency for all page classes to have get() method, because few people make their own method names, such as navigate(), goto() which makes it inconsistent for all pages.

What approach do you follow? Feel free to drop in your thoughts or suggestions if any. See you in the next one.