A Fluent Page Object Approach – return this

Another simple tip, but I use this a lot.

Return ‘this’ from your Page Object methods

 

e.g.

QueNessSlowLoadingExamplePage page4 = 
       new QueNessSlowLoadingExamplePage(driver);
 page4.get();

page4.
 setName("Bob Dobbs").
 setEmail("b.dobbs@mailinator.com").
 setMessage("Hello There").
 sendMessage();

In the above code I have a Page Object called QueNessSlowLoadingExamplePage which handles this example over at QueNess.com

And you can see that the setName, setEmail and setMessage are all ‘fluent’ in the sense that they return ‘this’ so I can chain them when using the Page Object.

e.g.

public QueNessSlowLoadingExamplePage setName(String name) {
 driver.findElement(NAME_FIELD).sendKeys(name);
 return this;
}

public QueNessSlowLoadingExamplePage setEmail(String email) {
 driver.findElement(EMAIL_FIELD).sendKeys(email);
}

On returning ‘other’ Page Objects

Note that this does not mean that I advocate returning ‘other’ Page Objects from a Page Object

i..e if ‘sendMessage’ causes a chain of system events such that a new page is displayed in the browser then I don’t advocate returning that page as a Page Object from the ‘sendMessage’ object.

If navigating to a new page was a possibility I would make ‘sendMessage’ a ‘void’ object, because I treat ‘Navigation’ as a separate responsibility, managed by a separate set of Objects.

On returning child components

Note that this does not mean that I never return ‘child’ or ‘container’ Page Objects.

e.g. if this page had a Widget, that I had modelled as a separate Page Object, I am quite happy to return a ‘new Widget’ Object from a method on this Page Object, because this Page Object would be acting as a container for that one.

Not an absolute rule

But not navigation.

Not for me.

At least, most of the time, because all rules are made to be broken, thereby granting us the requisite variety we need to model our applications effectively. So sometimes I may choose to navigate via Page Objects. Very often I do this for expediency, or for throw away code. I’ll very likely refactor it out later.

You can find code that illustrates this on github in my WebDriverExperiments repository.

Is this what you do?

Is this what you do? Leave a comment if you have any other Page Object Method tips.

This entry was posted in Practices, Selenium Simplified Blog, WebDriver. Bookmark the permalink.

9 Responses to A Fluent Page Object Approach – return this

  1. David says:

    Great informative post. We kept our page object modeling simple and our page objects never return other page objects. Each page object has to be manually instantiated and we leave it to the test author to determine navigation of page objects and (valid/invalid) page state. Our page objects only return non-page objects and simple data types (like for asserting data/state against).

    Why we exactly did this, not sure, I wasn’t the framework developer/maintainer, but it at least has keep the approach simple for us.

    And as food for thought, I would say it doesn’t really matter if a page object returns another page object or not, just a matter of simplifying test management. Whichever way, you should check that page state is correct (on correct page), and as a test author, one should be expected to know what page you are supposed to be at for every step of the test, just the same way for manual testing.

  2. Andreas says:

    I tend to return PageObjects in what I call – well, just now – the “wizard” pattern. If you start the ‘new registration’ type of wizard on a generic web page, you will always get the same pages (1 > 2 > 3 > 4) in the same order. So in that case I let page 1 return the page 2 PageObject on the method that does the submitting (e.g. page.next() or something like that).

    In nearly all other cases I try to avoid return PageObjects (except the object itself, as you explained) since it’s up to the test author to decide which page he expects and not to the framework developer (which in most cases is the same person… me, but with a different hat on).

    Thanks for the informative read!

    • Alan says:

      Thanks Andreas,

      If I was reviewing it, I think I might want to suggest a Wizard object that sits across the page flow. But I can also imagine circumstances where I might add those methods to the page objects themselves initially when creating it. I’d have to see the app and code base to determine if that was really appropriate or not.

      Choices. Choices. 🙂

      And all of our choices contextually based on the app, our code base, our skill sets, our coding biases, where we are in the project timeline, how long the code will survive, etc. etc.

      Good to hear about decisions being made when building the abstractions layer. I love hearing about alternative approaches and the thought processes used when deciding how to build them.

      Alan

  3. I’ve written about DRY page objects on my blog.

  4. James Affleck says:

    I don’t see the problem with returning Page Objects with methods.

    This does mean though that you may need to double up methods like login(user, pass) and loginAndFail(user, pass), if you have the bandwidth to test non-happy paths in your tests.

    In my opinion there’s not much problem in returning another page object, your tests just need to be sure that the next page is loaded to satisfaction before you’ll be interacting with that.
    How do I do that? Using a webdriver wait for some indicator for me to know that the next page is loaded (like a h1 being loaded, or correct breadcrumb appears or the like).

    This way your test will croak when expected, after you click submit and the next item doesn’t appear, your test will fail as expected.

    I do something similar to what Burdette Lamar proposes on his blog.
    Have a base page class, with some components that frequently occur like menu bar.
    Then have a MenuBar class, and in the BasePage class have a method

    public abstract class BasePage {
    public BasePage() {
    private static final WebDriver driver = DriverManager.getDriver();
    PageFactory.initElements(driver, this);
    //this is mostly in child classes
    // but including it as an example. You want a static method to get drivers so you never have to // create an instance of a PageClass and include the driver. You can just use MyClass clazz = new MyClass();
    // If you’re multi-threading wrap this in a ThreadLocal and you get an equally simple approach.
    }
    public MenuBar menu() {
    return new MenuBar();
    }
    public PageTab tab() {
    return new PageTab(); //etc
    }

    }

    This makes it easy to access frequently used sections in test classes or from the classes themselves.

    • Alan says:

      Hi James,

      Thanks for the Comment.

      Automating requires us to make decisions about the approaches we take.

      I decided that:

      – I didn’t like changing the Page Objects when site navigation changed, so I separate Navigation from actions on the page
      – I wanted Page abstractions to model the physical world rather than user domain, so ‘login’ would fit into user domain and ‘submit login form’ would fit into physical domain
      – I want to flexibility about Driver usage so I inject the driver when instantiated rather than having the Page ask the DriverManager for the Driver

      I tend to create high level abstractions for most used functionality e.g. my User object might have a login method since I use that a lot, but failed login I use less frequently so probably wouldn’t model that on the Page Object because that would add code the the Object that I rarely use. I might have a set of Workflow or Process objects that support me in doing that – or more likely I would use the ‘submit login form’ directly.

      Sometimes I model the ‘login form’ separate from a ‘Login Page’. e.g. if we were going to engage in A/B testing or have login form in different places on page, or multiple pages.

      I tend not to use BasePage objects. I do create objects for MenuBar and SideBar, sometimes I have methods on pages to return a MenuBar object, but mostly I’ll instantiate the MenuBar directly in the @Test. Depends on what I think works best for the app I’m testing and the type of @Test code we write.

      I mostly default to what I think provides most flexibility long term. And then wrap Objects and code around that for convenience.

      Different decisions for different projects.

Leave a Reply

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