Using the LoadableComponent pattern for better Page Object handling in Selenium

I’m sure that by now, everybody who is at least remotely involved in the creation of automated user interface tests using Selenium WebDriver is aware of the Page Object pattern (read more about it here and here) for introducing modularity and reusability in their automated tests. In this post I will introduce the LoadableComponent pattern, an extension to this pattern that standardizes handling the loading and verifying loading status of these page objects.

Why use the LoadableComponent pattern?
Out of the box, Selenium WebDriver does a good job of determining whether an HTML page has been loaded by using the document.readyState property, which is part of the W3C WebDriver specification. However, this property alone is not always enough to assert that all dynamic content on your page has been fully loaded and that all elements required in your test script are present on the page. This is especially the case when your web page uses a JavaScript-heavy framework such as the popular AngularJS or KnockoutJS frameworks.

So, even when Selenium tells you the page is loaded and continues to execute the next step in your test, this step might fail because the actual element needed for that step is not yet visible, clickable or otherwise ready. You can solve this problem to an extent by using suitable wrapper methods for the standard Selenium API methods, but wouldn’t it be nice to enhance our Page Objects with a generic approach to evaluate page load status? This is where the LoadableComponent pattern comes in.

Introducing the LoadableComponent pattern
To explain the concept of the LoadableComponent pattern, we will again turn to the ParaBank application. LoadableComponent is a base class in Selenium, which means that you can simply define your Page Objects as an extension of the LoadableComponent class. So, for example, we can simply define a LoginPage object as follows:

public class LoginPage extends LoadableComponent<LoginPage> {
  // class implementation will be explained later 
}

This does nothing more than defining this class as a LoadableComponent that loads the LoginPage page.

Now, by having our Page Objects extend the LoadableComponent base class, we need to implement two new methods, load() and isLoaded() (note that in C#, these are called ExecuteLoad() and EvaluateLoadedStatus() for some reason). These methods provide the added value of using the LoadableComponent pattern. The load() method contains the code that is executed to navigate to the page, while the isLoaded() method is used to evaluate whether we are on the correct page and whether page loading has finished successfully. Using LoadableComponent, our LoginPage class now looks like this:

public class LoginPage extends LoadableComponent<LoginPage> {
	
	private WebDriver driver;
	
	public LoginPage(WebDriver driver) {
		
		this.driver = driver;
		driver.get("http://parabank.parasoft.com");
	}
	
	@Override
	protected void isLoaded() throws Error {
		
		if(!PageLoad.myElementIsClickable(this.driver, By.name("username"))) {
			throw new Error("LoginPage was not successfully loaded");
		}
	}

	@Override
	protected void load() {		
	}
}

Note the use of a custom wrapper boolean method myElementIsClickable() to determine whether the page has loaded successfully. It can be used as a generic way to specify which element must be present for any given Page Object in order to consider it fully loaded:

public static boolean myElementIsClickable (WebDriver driver, By by) {
		
	try
	{
		new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(by));
	}
	catch (WebDriverException ex)
	{
		return false;
	}
	return true;		
}

Also, for most pages I don’t actually put code inside the body of the load() method. This is because in a typical application, most Page Objects are only accessed by means of navigation via other Page Objects instead of being accessed directly. Even for the LoginPage I have included the navigate() call in the constructor, because in this way we do not need to wait for the timeout in isLoaded() to be exceeded before the navigate() method is called from within load(). The example project I will link to at the end of this post shows you how I implemented the LoadableComponent pattern in my latest project.

So, now that we’ve implemented our Page Objects as LoadableComponents, we can use it in our tests simply by doing this:

new LoginPage(driver).get();

The get() method is implemented as follows:

public T get() {
	try {
		isLoaded();
		return (T) this;
	} catch (Error e) {
		load();
	}
 
	isLoaded();
 
	return (T) this;
}

In plain English: it calls isLoaded() first to see whether the page is loaded. If this is not the case, it calls load() to load the page. Afterwards, it calls isLoaded() again to see if the page is now successfully loaded.

Leveraging LoadableComponents in your tests
To ensure in your tests that your Page Objects are loaded before moving on, you can simply call get() each time a Page Object is instantiated:

@Test
public void validLoginTest() {

	// Load login page
	LoginPage loginPage = new LoginPage(driver).get();

	// Log in using valid credentials
	HomePage homePage = loginPage.correctLogin("john", "demo").get();

	// Load home page and check welcome text
	Assert.assertEquals("Welcome text is correct","Welcome John Smith", homePage.getWelcomeString());
}

The assertion in isLoaded() will fail if MyElementIsClickable() returns false, leading to the test as a whole to fail:

LoadableComponent error

Enhancing the LoadableComponent class
Finally, another interesting use of the LoadableComponent pattern is writing your own implementation of the class. In that way you can, for example, generate specific logging to the console or to an ExtentReports report in case you use it.

The source code for the original LoadableComponent implementation can be found here. A simple addition is writing the error message that is thrown by isLoaded() (which in turn is the error message thrown by the performed assertion) to the console:

public abstract class CustomLoadableComponent<T extends CustomLoadableComponent<T>> {

	@SuppressWarnings("unchecked")
	public T get() {
		try {
			isLoaded();
			return (T) this;
		} catch (Error e) {
			// This is the extra line of code 
			System.out.println("Error encountered during page load: " + e.getMessage());
			load();
		}

		isLoaded();

		return (T) this;
	}

	protected abstract void load();

	protected abstract void isLoaded() throws Error;
}

Now you can simply define your Page Objects as an extension of the CustomLoadableComponent class:

public class LoginPage extends CustomLoadableComponent<LoginPage> {
  // class implementation remains the same 
}

When we run our tests and encounter an error, we see that the error message is written to the console:
Custom LoadableComponent output

A working Eclipse project (created using Maven) containing all of the code samples included in this post can be downloaded here. Just run the tests in the tests package to see for yourself how it works.

15 thoughts on “Using the LoadableComponent pattern for better Page Object handling in Selenium

  1. You said that:

    “The isLoaded() method uses JUnit Assert methods to evaluate the loaded status. This is something I don’t really like about the LoadableComponent implementation in Java, as it creates an unnecessary dependency on JUnit.”

    I may be misunderstanding, but that doesn’t seem right. The isLoaded() method throws a java.lang.Error, which is compatible with JUnit asserts, but doesn’t require their use. You could just throw a new Error(), couldn’t you?

    • Hmm… I didn’t think of that to be honest, I must admit I followed the example I saw on the SeleniumHQ homepage. I’ll try your suggestion ASAP and fix this post and the code if it turns out you’re right. Thanks for the suggestion!

    • Hey Mark,

      thanks again for the suggestion. Both the post and the code have been updated:

      • Removed unwarranted rant about JUnit dependency
      • Changed code to simply throw an Error when myElementIsClickable returns false
      • Updated project to use TestNG instead of JUnit (there’s no need to use JUnit anymore and I prefer TestNG
      • Updated images in post to reflect new results
  2. Pingback: Testing Bits – 1/24/16 – 1/30/16 | Testing Curator Blog

  3. Pingback: Some kick ass blog posts from last week #4 - Mr.Slavchev()

  4. Pingback: Java Testing Weekly 5 / 2016

  5. Pingback: Java Web Weekly 110 | Baeldung

  6. I am trying to implement this CustomLoadableComponent but I am having an issue.

    When I login to my application, a new window opens, giving me two distinct windows.

    The test is failing at isLoaded because I think selenium cannot verify the page loaded correctly due to the other window. But I don’t know how to deal with the other window before isLoaded runs. I have tried numerous methods to get the window handles but nothing has worked. Have you used this CustomLoadableComponent pattern when passing handles around?

    • Hey BB,

      That’s a tricky one. No, I haven’t encountered this myself.. And to be honest I don’t know whether LoadableComponent is very suited to handle this situation..

      Out of curiosity: why is your application doing that? Seems like a bit of a design flaw.. Or is there more to your login logic than a simple session or cookie?

      • The application is in the emergency management space, and two windows open: the first for monitoring incoming incidents and the second for displaying a live map that shows the location of the incidents. Upon login, the main monitoring window opens slightly before but almost simultaneously as the map window opens.

        The other option I have tried other than getting the window handles is by trying to get Firefox to load the other window in a new tab instead, but I haven’t been successful at that either, as easy as that sounds.

        Thanks for your reply!

        • Hey BB,

          this might be doable but the code in your isLoaded() method (which checks whether the page has loaded) is going to get a little more complex. You can switch between windows in Selenium using driver.switchTo().window(windowHandle) where windowHandle is the handle of the new window. See also this StackOverflow page. You could use that code to loop through all windows, check in each window whether you’re in the right one (this is where it gets complicated though, because Selenium will generate a timeout whenever you’re NOT in the right window, leading to your test failing before it gets to the correct window) and proceed from there. It isn’t trivial though and you’d have to make sure it doesn’t result in a lot of false negatives..

          Either that, or assume that a click on the login button always opens another window and switch to that outside of the isLoaded() method. Then, you can just use LoadableComponent in the same manner as when there would have been only one window.

          Hope that helps a bit..

          • Thanks Bas! I did actually get this working using your advice. It seems simple after looking at it now…

            public TestPage(WebDriver driver) {
            super();
            this.driver = driver;
            this.driver.switchTo().defaultContent();
            }

            @Override
            protected void isLoaded() throws Error {
            PageFactory.initElements( driver, this );
            String window = driver.getTitle();
            Assert.assertEquals(“test”, window);
            }

            Thanks again for your help!

          • Excellent! You know you’ve found a good solution when it looks simple afterwards. The road to the simple solution can still be a hard one, though!

Leave a Reply

Your email address will not be published. Required fields are marked *