UI Automation for mortals: simple yet elegant Page Objects using Java and Selenide

One of the most discussed topics in UI Test Automation is Page Object Model. Page Object Model is a de-facto industry-standard pattern. This pattern was introduced by Martin Fowler in 2013 [1] and is an adaptation of Facade and Adapter patterns to the UI Test Automation.

Page Object Model helps to decouple business logic from implementation, so instead of brittle and unreadable

Bad UI test example

we would write something shorter and much easier to comprehend:

A better UI test example

Traditionally, due to Selenium Web Driver low-level API writing Page Objects was not an easy task. However, it may be done in a fast and elegant way using Selenide, which is the best Selenium Web Driver wrapper for Java that I am aware of at the moment. Selenide offers:

  • Concise fluent API for tests
  • Ajax support for stable tests out of the box
  • Powerful selectors, and
  • Simple configuration

Using Selenide, you will no longer have to worry about shutting down browser, handling timeouts or StaleElement Exceptions. You will just concentrate on testing your application business logic without unnecessary technical troubles.

Application Under Test

TODO-List application

This application conveniently consists of only one page, so we will need to implement only one Page Object for it.

Designing Page Object

Disregards of what tool you use, the one of the most important things is Page Object API design.

Many people would start from looking at the page internals (i.e. HTML code), but I would argue that is is a wrong place to start. Instead, we should better to think how we going to interact with the page. By doing this we’re making sure that the Page Object will be convenient and easy to use.

In Java, for example, we could start by creating an interface:

If you have played with the application a bit you can immediately notice that I have decided to design a fairly low-level API for a Page Object. I did this so I could follow the three-layer framework architectural pattern. However, it is not necessarily has to be this way, and one can design a little bit more high-level API Page Object, especially with the help of Selenide.

Why Interface?

Interface may be useful if we consider possibility of reusing the same test scenarios for different flavours of the same application, for example for Web-version (driven via Selenium/Selenide) and mobile application (driven by Appium)

On the other hand, if we don’t consider such possibility, it may be absolutely OK to not to use interlace.

I used interface mostly because it helps me to think of API and not to think about implementation before it is absolutely necessary.

Anyway, by starting with interface definition we do two things at once:

  • Design (and think of) an API for a Page Object
  • Make it possible to start writing tests or other dependent code before Page Object is fully implemented .

Implementing Page Object

// Would return one element:
$(By.selector);
// Would return list of elements:
$$(By.selector);

By using those simple method, we can, for example, interact with elements this way:

private static final By NEW_FIELD_XPATH = By.xpath("//input[@id='new-todo']");@Override
public void setNewItemName(String todoName) {
$(NEW_FIELD_XPATH).setValue(todoName);

}

Putting it simpler — by using those two magic methods one can locate and interact with elements without all the Selenium boilerplate! And even more, from the Selenide documentation:

The majority of operations on elements, acquired by the $ and $$ commands, have built-in implicit waits depending on a context. This allows in most cases to be not distracted by handling explicitly the waiting for loading of elements while automating testing of dynamic web applications [2].

Putting boring things aside (like selecting a proper locator for elements) we may end up with a Page Object like this:

Notice how many things we have out of the box without any hassle:

// to find element, instead of
// driver.findElement(By.cssSelector(“h1”))
$(“h1”)
// Instead of
// assertTrue(
// driver.findElement(By.cssSelector(“h1”)
// .isDisplayed()
// )
// we just
$(“h1”).shouldBe(visible)
// to type into inbox element
$(NEW_FIELD_XPATH).setValue(todoName);
// to emulate enter press
$(NEW_FIELD_XPATH).pressEnter()
// to select-unselect element
findCheckBoxForItem(todoItemName).setSelected(true);
// to emulate hovering mouse
$(todoItemXpath).hover();

In addition, Selenide can:

  • Do all the browser manipulations (such as starting and closing the browser) easy way
  • Make screenshots on failures
  • Profile tests using integrated profiler
  • Take car of test parallelization for you

And of course it is not a whole list of nice things Selenide has.

With the Selenide available there’s no real reason to use plain Selenium in Test Automation Frameworks. With the current rate of development I consider Selenide not a Selenium wrapper, but pretty much an alternative.

References:

[1] https://www.martinfowler.com/bliki/PageObject.html

[2] https://selenide.org/documentation.html