2

Java + Webdriver + PageObject. I have a question about pages organization.

At this moment I have class which contains all pages in the project. At start all pages are initializing, and it looks like this:

public class AllPages {

    private AddFilesPage addFilesPage;
    private ConfirmDialogPage confirmDialogPage;

    public AllPages(WebDriver webDriver, WebDriverWait wait) {
        addFilesPage = new AddFilesPage(webDriver, wait);
        confirmDialogPage = new ConfirmDialogPage(webDriver, wait);
    }

    public AddFilesPage getAddFilesPage() {
        return addFilesPage;
    }

    public ConfirmDialogPage getConfirmDialogPage() {
        return confirmDialogPage;
    }

}

I wonder - what if I will do remake like this:

public class AllPages {

    private WebDriver webDriver;
    private WebDriverWait wait;

    public AllPages(WebDriver webDriver, WebDriverWait wait) {
        this.webDriver = webDriver;
        this.wait = wait;  
    }

    public AddFilesPage getAddFilesPage() {
        return new AddFilesPage(webDriver, wait);
    }

    public ConfirmDialogPage getConfirmDialogPage() {
        return new ConfirmDialogPage(webDriver, wait);
    }

}

Pros:

  • I think it easier to add new pages - just one getter, instead of field, create in constructor and getter
  • in page constructor I can add wait for elements, so when I use page it automatically wait for loading

Cons:

  • slower, because we not reuse pages (will init every time we call it). But does it critical?
  • maybe another disadvantages, could you please tell what you think?
2

No, no and no. :) Do not initialize all your page objects at the start, what if your application keeps growing and growing. We already have hundreds of page objects, this could potential slow down all your tests in the suite.

We always use the following way to access a Page: "Methods return other PageObjects"

Example from Ubuntu acceptance testing:

An UAT checks a user story. It will involve the journey of the user through the system, so he will move from one page to another. Lets take a look at how a journey to reset the stop watch will look like:

stopwatch = clock_page.open_stopwatch() 
stopwatch.start()
stopwatch.reset()

This means you only instantiate pages when you are going to use them, after an action results into a new page being visible or opened.

Read their full article to get an idea how the Ubuntu team implements the PageObject pattern: https://developer.ubuntu.com/en/start/platform/guides/acceptance-testing-using-the-page-object-model/

0

YAGNI: You ain't gonna need it. So why do extra work if you will not need it?

Link from "original wiki", created by Ward Cunningham in 1995 and recommended read for all software developers:

YAGNI: "Always implement things when you actually need them, never when you just foresee that you [may] need them."

  • What do you mean by "extra work"? To remake class with all pages? – Pasha Jun 23 '15 at 12:19
  • Extra work is instantiating all those other pages your test will never touch. – Peter M. - stands for Monica Jun 23 '15 at 12:20
  • This is more a comment then an answer, maybe you can explain the YAGNI pattern and how this solves this anti-pattern? – Niels van Reijmersdal Jun 23 '15 at 13:46
  • It is not much of a pattern but just a rule of thumb: Link added. – Peter M. - stands for Monica Jun 23 '15 at 14:27
0

This is an interesting question that I have been pondering myself. I am currently using a PageObject design using PageFactory IWebElements (C#).

I do not follow this requirement: "Methods return other PageObjects".

I will share my design and let others decide if this approach is "also" bad. This approach may do what you are trying to do but a little cleaner. Tons of ways you can do this.

Currently my design is top level class files (like your "AllFiles" but smaller) with all page objects included in that "web" portion or pages to help make test scripts/cases easier to write for the user who doesn't know how to program.

So,

MyAccount.cs

  • MyAccountPage
  • MySettingsPage
  • etc...

MainDomainPage.cs

  • Different pages...

Each page could consist of

MyAccountPage.cs

  • Header (common menu links that is contained in ALL account pages)
  • Other Elements for page...

Soo my unit tests of MyAccount are all in the same unit test file. (pseudo code)

        [SetUp]
        public void TestSetup()
        {
            new TWebDriver();//Autogenerates the TestFixture Browser type
            MyAccount = new MyAccount(DomainType); //we use different   domains/servers for the same page sometimes. Instantiate all pages inside MyAccount    
        }
        [Test]
        public void MyTestIsLoaded(){
            MyAccount.MyAccountPage.LoadPage();
            //Navigate to My Settings page.
            MyAccount.MyAccountPage.Menu.MySettings.click();
            Assert.True(MyAccount.MySettingsPage.isLoaded());

            //Navigate back to MyAccountPage just to show how my code kind of works
            MyAccount.MySettingsPage.Menu.MyAccount.click();
            //Navigated to MyAccount page by clicking the menu link "MyAccount"
        }

If I was redirected from myaccount to the MainDomainPage I would also have to instantiate it.

MainDomainPage= new MainDomainPage(DomainType);

The reason I have chosen this design of not returning other page objects is because I have a library that mimics all elements in a webpage. I prefer to have one function for clicking a button/login/search results. Not expected success/failure ones.

public abstract WebPageElement.cs

Extends WebPageElement

  • TextBox
  • Label
  • Link
  • Image
  • LinkImage
  • Button

etc... Each element contains specific functions related only to its object, unlike selenium where it contains all methods even if it doesn't relate.

All pages (but not partial pages such as common code shared between pages as they are just classes defined inside a page object file. eg. A popup dialog (widget) would not be a page but a class defined inside a page.) extend my written PageObject.cs class which includes generic functions and variables.

    internal static IWebDriver _DRIVER;
    private static IWait<IWebDriver> _wait;
    Methods()...

Here all pages will share the same driver and wait object. This allows test scripts to change the timeout as needed.

This leaves each page object to include just the elements found on the page.

       public Constructor(){
        PageFactory.InitElements(PageObject._DRIVER, this);//pageobject is the base class but since its static must be referenced directly
        Image = new LinkImage(_image, _imagelink, _imgHow, _imglHow); //IwebElement, IWebElement, string, string
        UserName = new TextBox(_username, _unHow); //IWebElement, string where the values are.
       }

    public LinkImage Image { get; protected set; }
    public TextBox UserName { get; protected set; }

PageFactory portion:

 private const string _unHow = "UserName";
 private const string _imgHow = "Password";

 [FindsBy(How = How.Id, Using = _imgHow)] private IWebElement _image;
 //Code deleted
 [FindsBy(How = How.Id, Using = _unHow)] private IWebElement _username;

This makes representing a webpage very fast for code development.

I understand the concept of not instantiating things not used which this does do but I have found this to be a simpler option for writing test cases where we want no code knowledge. The extent it does is wethere the global file MyAccount is instantiated in TestCaseSetup() vs TestFixtureSetup() that is shared by most unit tests.

Thoughts? Bad design?

I would love input on this.

I have not read a convincing argument to stay away from this approach as the pages are separated into common groups (to avoid 100+ page objects in one file). I learned about this design (using WebPageElement objects to wrapper all functions for each type of object like a button) from an online article by a professor that used something similar.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.