Hi all, in this article I will explain you the loadable component pattern in Page Object Model architecture. In our test automation projects generally, we are doing some prerequisite actions to test some scenarios. For example, when we are testing the login, first we need to open the homepage and go to login page, then start to do login actions. Without the loadable component pattern, we do this test as follows:
@Test (priority = 0, description="Invalid Login Scenario with wrong username and password.") public void invalidLoginTest_InvalidUserNameInvalidPassword () { //*************PAGE INSTANTIATIONS************* HomePage homePage = new HomePage(driver,wait); LoginPage loginPage = new LoginPage(driver,wait); //*************PAGE METHODS******************** //Open N11 HomePage homePage.goToN11(); //Go to LoginPage homePage.goToLoginPage(); //Login to N11 loginPage.loginToN11(wrongUsername, wrongPassword); //*************ASSERTIONS*********************** loginPage.verifyLoginPassword(("E-posta adresiniz veya şifreniz hatalı")); }
When we apply the loadable component pattern in our Page Object Model framework, our test class will become like this:
@Test (priority = 2, description="Invalid Login Scenario with wrong username and password.") public void invalidLoginTest_InvalidUserNameInvalidPassword () { //*************PAGE INSTANTIATIONS************* homePage = new HomePage(driver); loginPage = new LoginPage(driver, homePage); //*************PAGE METHODS******************** //Open N11 HomePage and Go to Login Page loginPage.get(); //*************PAGE METHODS******************** loginPage.loginToN11(wrongUsername, wrongPassword); //*************ASSERTIONS*********************** loginPage.verifyLoginPassword(("E-posta adresiniz veya şifreniz hatalı")); }
As you see above, now we do not need to specify prerequisite actions explicitly. I mean, we do not need to write HomePage class’s methods which opens the login page:
//Open N11 HomePage homePage.goToN11(); //Go to LoginPage homePage.goToLoginPage();
So, how can we use Loadable Component Pattern? Let’s start to learn.
First, we need to extend our page classes with LoadableComponent abstract class.
//HomePage Class public class HomePage extends LoadableComponent<HomePage> { ..... ..... ..... } //LoginPage Class public class LoginPage extends LoadableComponent<LoginPage> { ..... ..... ..... }
LoadableComponent class’s structure is shown below:
public abstract class LoadableComponent<T extends LoadableComponent<T>> { public LoadableComponent() { } public T get() { try { this.isLoaded(); return this; } catch (Error var2) { this.load(); this.isLoaded(); return this; } } protected abstract void load(); protected abstract void isLoaded() throws Error; }
It is an abstract class with load(), isLoaded(), and get() methods. When we extend this class, we have to implement load(), and isLoaded() methods and we can use get() method to obtain our class when it is loaded based on our implementations.
After extending LoadableComponent class, now we need to implement load() and isLoaded() methods in HomePage and LoginPage classes. In load () method, we need to declare the actions which load the page and in isLoaded() method, we need to check that the page has been loaded or not. Before this, let’s look at the project structure:
BasePage is our common Page class which comprises of the common functions of all pages.
HomePage is home page class.
LoginPage is login page class.
TestListener is our test listener Class for taking action when test and suite started, finished, failed, skipped, passed.
BaseTest is the common test class.
LoginTest is login test class.
ExtentManager is for ExtentReports.
Let’s look at some classes’ details. First, let’s start with BasePage. We declared common methods such as click, sendText, getText, etc.
package pages; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.support.ui.WebDriverWait; public class BasePage { private WebDriver driver; //Constructor public BasePage (WebDriver driver){ this.driver = driver; } //Click Method public void click (By elementLocation) { driver.findElement(elementLocation).click(); } //Write Text public void writeText (By elementLocation, String text) { driver.findElement(elementLocation).sendKeys(text); } //Read Text public String readText (By elementLocation) { return driver.findElement(elementLocation).getText(); } }
HomePage Class:
Here, I implemented load() and isLoaded() methods which belong to LoadableComponent abstract class. I also instantiate BasePage class’s object in the constructor to use its methods via basePage object.
package pages; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.support.ui.LoadableComponent; import org.openqa.selenium.support.ui.WebDriverWait; import static org.testng.AssertJUnit.assertTrue; public class HomePage extends LoadableComponent<HomePage> { //*********Page Variables********* private String baseURL = "https://www.n11.com/"; private WebDriver driver; private WebDriverWait wait; private BasePage basePage; //*********Constructor********* public HomePage (WebDriver driver) { this.driver = driver; this.wait = new WebDriverWait(driver, 10); basePage = new BasePage(driver); } //*********Web Elements********* By signInButtonBy = By.className("btnSignIn"); //*********Override LoadableComponent Methods********* //We need to go to the page at load method @Override protected void load() { this.driver.get(baseURL); } //We need to check that the page has been loaded. @Override protected void isLoaded() throws Error { assertTrue("HomePage is not loaded!", driver.getCurrentUrl().contains(baseURL)); } //*********Page Methods********* //Go to LoginPage public LoginPage goToLoginPage (){ basePage.click(signInButtonBy); return new LoginPage(this.driver, this); } }
LoginPage Class:
Here, I implemented load() and isLoaded() methods which belong to LoadableComponent abstract class. I also instantiate BasePage class’s object in the constructor to use its methods via basePage object. I also use HomePage class’s goToLoginPage() method at load() block. In order to reach that method, I need to get that class as parent. I assigned the parent object in the constructor and use it at load() method.
parent.get() -> gets the HomePage class
and then, I used its goToLoginPage() method as -> parent.get().goToLoginPage()
In this way, I can go to LoginPage() class.
After, going to LoginPage, we need to check that the page is loaded? We can check this at isLoaded() method.
assertTrue(“LoginPage is not loaded!”, driver.getCurrentUrl().contains(loginURL));
package pages; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.LoadableComponent; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.Assert; import static org.testng.AssertJUnit.assertTrue; public class LoginPage extends LoadableComponent<LoginPage> { //*********Page Variables********* private WebDriver driver; private WebDriverWait wait; private BasePage page; private LoadableComponent<HomePage> parent; private final String loginURL = "https://www.n11.com/giris-yap"; //*********Constructor********* public LoginPage (WebDriver driver, LoadableComponent<HomePage> parent) { this.driver = driver; this.wait = new WebDriverWait(driver,10); page = new BasePage(this.driver); this.parent = parent; } //*********Web Elements********* By usernameBy = By.id("email"); By passwordBy = By.id("password"); By loginButtonBy = By.id("loginButton"); By errorMessageUsernameBy = By.cssSelector("#loginForm .error:nth-of-type(1) .errorMessage"); By errorMessagePasswordBy = By.cssSelector("#loginForm .error:nth-of-type(2) .errorText"); //*********Override LoadableComponent Methods********* //We need to go to the page at load method @Override protected void load() { parent.get().goToLoginPage(); } //We need to check that the page has been loaded. @Override protected void isLoaded() throws Error { assertTrue("LoginPage is not loaded!", driver.getCurrentUrl().contains(loginURL)); } //*********Page Methods********* public void loginToN11 (String username, String password){ //Enter Username(Email) page.writeText(usernameBy,username); //Enter Password page.writeText(passwordBy, password); //Click Login Button page.click(usernameBy); //In order to click right, this line needed. Site related. page.click(loginButtonBy); } //Verify Username Condition public void verifyLoginUserName (String expectedText) { wait.until(ExpectedConditions.visibilityOfElementLocated(errorMessageUsernameBy)); Assert.assertEquals(page.readText(errorMessageUsernameBy), expectedText); } //Verify Password Condition public void verifyLoginPassword (String expectedText) { wait.until(ExpectedConditions.visibilityOfElementLocated(errorMessagePasswordBy)); Assert.assertEquals(page.readText(errorMessagePasswordBy), expectedText); } }
Now, let’s go on BaseTest class. In this class, we can declare the before and after operations at class level.
BaseTest Class:
package tests; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; public class BaseTest { public WebDriver driver; public WebDriverWait wait; @BeforeClass(description = "Class Level Setup!") public void classLevelSetup () { //Create a Chrome driver. All test classes use this. driver = new ChromeDriver(); //Create a wait. All test classes use this. wait = new WebDriverWait(driver,15); //Maximize Window driver.manage().window().maximize(); } @AfterClass (description = "Class Level Teardown!") public void classLevelTeardown () { driver.quit(); } }
and now, it is time to write a test method.
LoginTest Class:
Here, I did comon prerequisites for each test in @BeforeMethod block. I can reach the loginPage with below line:
loginPage.get();
This method, firsts call’s LoginPage class’s load() method. In this method, parent.get() part calls HomePage class’s load() method and gets the HomePage class then calls its goToLoginPage() method to load LoginPage().
@Override
protected void load() {
parent.get().goToLoginPage();
}
In @Test blocks we can only call LoginTest page’s methods to fulfill login operation and test scenarios. In this way, we don’t need to explicity call HomePage class’s methods to go/open/load LoginPage.
package tests; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import pages.HomePage; import pages.LoginPage; public class LoginTests extends BaseTest { //Test Data private String wrongUsername = "onur@"; private String wrongPassword = "11122233444"; private HomePage homePage; private LoginPage loginPage; @BeforeMethod(description = "Method Level Setup!") public void methodLevelSetup() { //*************PAGE INSTANTIATIONS************* homePage = new HomePage(driver); loginPage = new LoginPage(driver, homePage); //*************PAGE METHODS******************** //Open N11 HomePage and Go to Login Page loginPage.get(); } @Test (priority = 2, description="Invalid Login Scenario with wrong username and password.") public void invalidLoginTest_InvalidUserNameInvalidPassword () { //*************PAGE METHODS******************** loginPage.loginToN11(wrongUsername, wrongPassword); //*************ASSERTIONS*********************** loginPage.verifyLoginPassword(("E-posta adresiniz veya şifreniz hatalı")); } @Test (priority = 1, description="Invalid Login Scenario with empty username and password.") public void invalidLoginTest_EmptyUserEmptyPassword () { //*************PAGE INSTANTIATIONS************* loginPage.loginToN11("",""); //*************ASSERTIONS*********************** loginPage.verifyLoginUserName("Lütfen e-posta adresinizi girin."); loginPage.verifyLoginPassword("Bu alanın doldurulması zorunludur."); } }
I do not want to share here ExtentManager and TestListener classes. They are not related to the Loadable Component pattern. But, you can find the full implemented project here: https://github.com/swtestacademy/loadablecomponentexample
You can also learn the SlowLoadableComponent Pattern via this article: https:/slowloadablecomponent-pattern/
[fusion_widget_area name=”avada-custom-sidebar-seleniumwidget” title_size=”” title_color=”” background_color=”” padding_top=”” padding_right=”” padding_bottom=”” padding_left=”” hide_on_mobile=”small-visibility,medium-visibility,large-visibility” class=”” id=””][/fusion_widget_area]
Thanks.
Onur Baskirt

Onur Baskirt is a Software Engineering Leader with international experience in world-class companies. Now, he is a Software Engineering Lead at Emirates Airlines in Dubai.
In my opinion, using Generic here is too much. Maybe we need two abstract method load() and isLoaded() for page model classes then we implement these two method accordingly to their intention.