Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Page Objects That Improve UI Automation

DZone's Guide to

Page Objects That Improve UI Automation

Page object patterns are fundamental to the creation of fast, robust, and readable UI automation. Now learn how to use a fluent API for page objects to improve your projects even more.

· DevOps Zone
Free Resource

The Nexus Suite is uniquely architected for a DevOps native world and creates value early in the development pipeline, provides precise contextual controls at every phase, and accelerates DevOps innovation with automation you can trust. Read how in this ebook.

If you have read some of my previous posts, most probably you have checked some of my articles about Design Patterns in Automation Testing. One of the most prominent patterns that I have blogged about is the Page Object Pattern. It is so fundamental to the creation of a fast, robust, and readable UI automation that I even dedicated to it a second article. In my team, we have applied it for a couple of years in different projects. During the process, we came up with a few different variations of the page objects. I believe that some of them lead to more readable and more maintainable code compared to the previously presented ones.

Page Objects Who Make Code More Readable

 

Page Objects v.2.0 – Interfaces

I like this pattern so much that I even created a fluent API for page objects. However, I think that its usage is a little bit troublesome, at least for me. It turned out that I am not a big fan of the Fluent Interface. If you have read my article on the subject, you may have found out that I suggested using the pages as singletons. Nonetheless, I think that there are more “cleaner” ways for doing this. The base singleton’s class and all other base generic pages make the code more unreadable and not so easy to grasp. We resolved this particular problem through the application of an IoC Container. Unfortunately, the page objects’ inheritance tree was still relatively complicated because of the two generic parameters.

IoC Container – Previous Version

public class BasePage<M>
    where M : BasePageElementMap, new()
{
    protected readonly string url;

    public BasePage(string url)
    {
        this.url = url;
    }

    public BasePage()
    {
        this.url = null;
    }

    protected M Map
    {
        get
        {
            return new M();
        }
    }

    public virtual void Navigate(string part = "")
    {
        Driver.Browser.Navigate().GoToUrl(string.Concat(url, part));
    }
}

public class BasePage<M, V> : BasePage<M>
    where M : BasePageElementMap, new()
    where V : BasePageValidator<M>, new()
{
    public BasePage(string url) : base(url)
    {
    }

    public BasePage()
    {
    }

    public V Validate()
    {
        return new V();
    }
}

After a couple of discussions about the assertion methods, should they be part of the page objects, I started thinking about how can I improve them. On one hand, I wanted the assertions to be reused, on the other hand, they somehow shouldn’t have been directly part of the page objects. I thought that because they were the reason for the existence of the second base class with the additional generic parameter. Also, there was a new requirement - the pages needed to be interchangeable. In summary, if you have a Page A, it is later deprecated, and its substitute is Page A1. It should be easy to exchange both implementations without breaking all the surrounding code.

Interfaces- Improved Version

public class BingMainPage : BasePage<BingMainPageMap>, IBingMainPage
{
    public BingMainPage(IWebDriver driver)
        : base(driver, new BingMainPageMap(driver))
    {
    }

    public override string Url
    {
        get
        {
            return @"http://www.bing.com/";
        }
    }

    public void Search(string textToType)
    {
        this.Map.SearchBox.Clear();
        this.Map.SearchBox.SendKeys(textToType);
        this.Map.GoButton.Click();
    }
}

To solve the last mentioned problem, we introduced a new participant of the Page Object Pattern page's interface. It defines what actions the page should be able to do.

public interface IBingMainPage : IPage
{
    void Search(string textToType);
}

Now all the code that depends on the page can use the page as an interface. That means if you have to replace it, the new version only needs to implement the same interface.

The second improvement that we made was related to the previously called Validators classes. In general, they were holding assertion methods, so the first thing that we decided to do was to rename them to end with suffix Asserter. We did that because in the production code the validators are meant for a different job, like validating user’s input, not asserting things.

After that, another big refactoring that we applied was that the Asserter’s methods were used as extension methods of the page’s interface. As a result, they can be used as ordinary methods provided by the concrete page.

public static class BingMainPageAsserter
{
    public static void AssertResultsCountIsAsExpected(this IBingMainPage page, int expectedCount)
    {
        Assert.AreEqual(page.GetResultsCount(), expectedCount, "The results count is not as expected.");
    }
}

The only drawback of the presented implementation is that you always need to create a wrapper method of the asserted element because you don’t have a direct access to the page’s element map through its interface. However, these ideas resulted in eliminating the second base page and the additional generic parameter.

public abstract class BasePage<TMap>
    where TMap : BaseElementMap
{
    private readonly TMap map;
    protected IWebDriver driver;

    public BasePage( IWebDriver driver, TMap map)
    {
        this.driver = driver;
        this.map = map;
    }

    internal TMap Map 
    {
        get
        {
            return this.map;
        }
    }

    public abstract string Url { get; }

    public virtual void Open(string part = "")
    {
        this.driver.Navigate().GoToUrl(string.Concat(this.Url, part));
    }
}

As you can see from the example, the inheritance model is simplified compared to the previous versions.

The usage of the provided solution is straightforward.

[TestClass]
public class BingTests 
{
    private IBingMainPage bingMainPage;
    private IWebDriver driver;

    [TestInitialize]
    public void SetupTest()
    {
        driver = new FirefoxDriver();
        bingMainPage = new BingMainPage(driver);
    }

    [TestCleanup]
    public void TeardownTest()
    {
        driver.Quit();
    }

    [TestMethod]
    public void SearchForAutomateThePlanet()
    {
        this.bingMainPage.Open();
        this.bingMainPage.Search("Automate The Planet");
        this.bingMainPage.AssertResultsCountIsAsExpected(264);
    }
}

The tests use the pages as in the previously presented versions. The only subtle detail here is that if you want to be able to use the extension assertion methods, you need to add the using statement for the namespace, their class is in.

Public Map





Page Objects v.2.1 – Public Map, Skip Interfaces

As pointed in the subtitle, the next “generation” of page objects expose their element map to the code, that uses the page. Also, we decided that it is an overhead to create an interface for every particular page. Moreover, we found out that most of the time the interfaces of the substitute pages should be different from the old ones because different changes are applied to the pages. So the first subtle adjustment that we made was in the base page class, marking the Map property as public.

public abstract class BasePage<TMap>
    where TMap : BaseElementMap
{
    private readonly TMap map;
    protected IWebDriver driver;

    public BasePage(IWebDriver driver, TMap map)
    {
        this.driver = driver;
        this.map = map;
    }

    public TMap Map 
    {
        get
        {
            return this.map;
        }
    }

    public abstract string Url { get; }

    public virtual void Open(string part = "")
    {
        this.driver.Navigate().GoToUrl(string.Concat(this.Url, part));
    }
}

The second alternation was connected with the Asserter classes. Now they don’t extend the pages’ interfaces rather the pages themselves.

public static class BingMainPageAsserter
{
    public static void AssertResultsCountIsAsExpected(this BingMainPage page, int expectedCount)
    {
        Assert.AreEqual(page.Map.ResultsCountDiv.Text, expectedCount, "The results count is not as expected.");
    }
}

The only difference of the usage in tests is that now you can access the Map’s elements directly in tests.

[TestMethod]
public void SearchForAutomateThePlanet()
{
    this.bingMainPage.Open();
    this.bingMainPage.Map.FeelingLuckyButton.Click();
    this.driver.Navigate().Back();
    this.bingMainPage.Search("Automate The Planet");            
    this.bingMainPage.AssertResultsCountIsAsExpected(264);           
}

In my opinion, this is a good idea only if the nitty-gritty framework’s logic is hidden behind the page objects. I mean actions like clearing the text input before real typing, executing JavaScript calls, and so forth.

Page Objects v.2.3 – Partial Pages

The thing that I love most in the page objects, that we designed, is that the elements, page’s logic, and assertions are placed inside different files. I believe that this makes the page objects a lot more understandable and readable. Additionally, it decreases the associated maintainability costs.

To simplify the inheritance tree even further, we decided to use partial classes instead of generic base classes. With the small adjustment of making the Map property public, we thought that it will be an excellent idea of putting the maps’ elements directly in the page. However, if they were combined in the same file, the design would have had the same drawbacks as the WebDriver’s one. It would have led to larger files where elements would have been mixed with the page’s service methods. We didn’t want that. The solution was to leave elements in a separate file, but now it is a partial class of the main page’s one.

public partial class BingMainPage : BasePage
{
    public IWebElement SearchBox
    {
        get
        {
            return this.driver.FindElement(By.Id("sb_form_q"));
        }
    }

    public IWebElement GoButton
    {
        get
        {
            return this.driver.FindElement(By.Id("sb_form_go"));
        }
    }

    public IWebElement ResultsCountDiv
    {
        get
        {
            return this.driver.FindElement(By.Id("b_tween"));
        }
    }
}

This is how the new page map looks like. It uses the driver instance defined in the main page class.

public partial class BingMainPage : BasePage
{
    public BingMainPage(IWebDriver driver) : base(driver)
    {
    }

    public override string Url
    {
        get
        {
            return @"http://www.bing.com/";
        }
    }

    public void Search(string textToType)
    {
        this.SearchBox.Clear();
        this.SearchBox.SendKeys(textToType);
        this.GoButton.Click();
    }
}

There are some small changes in the main page class too. Now it inherits a simpler version of the BasePage which doesn’t require a generic element map parameter.

The usage of the new page objects is identical to the previously presented with the only difference that the page’s elements can be accessed directly from the page’s instance.

[TestMethod]
public void SearchForAutomateThePlanet_Second()
{
    this.bingMainPage.Open();
    this.bingMainPage.SearchBox.Clear();
    this.bingMainPage.SearchBox.SendKeys("Automate The Planet");
    this.bingMainPage.GoButton.Click();
    this.bingMainPage.AssertResultsCountIsAsExpected(264);
}

Page Objects Pros

Pros

One of the biggest advantages of the proposed ideas is the single responsibility of the page’s classes.

In object-oriented programming, the single responsibility principle states that every class should have responsibility over a single part of the functionality, and that responsibility should be entirely encapsulated by the class.

In the presented implementation, the map class is responsible only for locating elements, the asserter class for asserting things, and the page itself for providing service methods.

Source Code

You can download the full source code from my Github Repository.

If you enjoy my publications, feel free to SUBSCRIBE 

Also, hit these share buttons. Thank you!  

References:

The DevOps Zone is brought to you in partnership with Sonatype Nexus.  See how the Nexus platform infuses precise open source component intelligence into the DevOps pipeline early, everywhere, and at scale. Read how in this ebook

Topics:
devops ,code quality ,testing ,automation ,page object pattern ,java

Published at DZone with permission of Anton Angelov, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}