Photo by Georgie Cobbs on Unsplash
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 haveget()
method, because few people make their own method names, such asnavigate()
,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.