Mani
Mani

Reputation: 1186

Page Object pattern desgin

I went to page object pattern following link and the concepts are nice ,

https://code.google.com/p/selenium/wiki/PageObjects

Here is my doubt :

public LoginPage typeUsername(String username) {
    driver.findElement(usernameLocator).sendKeys(username);
    return this;    
}

public LoginPage typePassword(String password) {
    driver.findElement(passwordLocator).sendKeys(password);
    return this;    
}


public HomePage submitLogin() {    
   driver.findElement(loginButtonLocator).submit();
    return new HomePage(driver);    
}

Above selenium calls are wrapped with some function and we are calling these function in testcase or any page level function like this:

 public HomePage loginAs(String username, String password) {

        typeUsername(username);
        typePassword(password);
        return submitLogin();
    }

My doubt is: why can't we directly call the sendkey or submit or click every time throughout the project. only one line code again we wrapped with our own function this will take some more time to run code.

is there any performance issue or not?

Upvotes: 3

Views: 285

Answers (3)

Raz Shuty
Raz Shuty

Reputation: 51

I think the question and your doubts revolves on the basic, and furthor more I will try to answer

What is Page Object design pattern exactly?

Well, Page Objects are some sort of an API to a page (or a view) you have in your system, lets think of it as more as “Services” the page gives the tester…

Once it is seen as so, I believe its easier to think of the motivation...

A Page Object knows the internal of its matching HTML page (or view) and it alone knows it…

For example, if we would like to perform login (as a user) we are provided with these services:

  • We type a username in the username input.
  • We can type a password in the password input.
  • We can click on the ‘Login’ button (Expecting failure or success).

Of course there are more “Services” given, but we can stop here and get back to the point… What are we supposed to do with these “Services”? Thats the easy part:

We need to create a “LoginPage” object that know the (private) selectors of the page and the HTML construct (ids, classes, xpaths and so on…). What is public to the tester? The services we just talked about above…

A tester can ask the login page to “TypeUsername(string inputUsername)” or TypePassword(string inputPassword) – You can also add “Services” like “LoginAsDefaultUser()” if you are using this page to test other pages…

The Problem

While this a good start, it leaves a lot of room for improvement.

The most obvious problem is that there are common actions we will likely need across multiple Page Objects. And with our current approach we would end up with duplicative code.

A Solution

When creating a BasePage Object that wraps Selenium core, and each page Inherit from this page (directly or in directly) we are creating a dynamic environment that is not coupled and if somewhere a page (or view) has change you need to fix only one specific object.

Summary

  • Even though it is called the Page Object design pattern, it is more as a View Object design pattern, for example: If in your platform there is a left menu that returns on every page you can create the “MyPlatformBasePage” and every view will inherit from it (and code duplication is gone yet again, Victory!)
  • Page Objects exposes ONLY the “Services” provided, do not expose Selenium internals.
  • Page Object is not a test, Do not assert on its results.
  • Methods return other PageObjects, For example: LoginPage that invoke the ‘LoginAsDefaultUser’ will navigate us to our applications home page so we must return our “HomePage” object.
  • Use clean code principals, different “Services” provided by you will be represented with different methods, do not pass booleans to change the logic of a “Service”.

Motivation

  • Everybody can write these tests now, even non technical QAs
  • The code now is completey dynamic and encapsulated
  • Using facade layer (or Base Page) you can potentialy replace Selenium or change version without a giant hassle.

Upvotes: 0

ekostadinov
ekostadinov

Reputation: 6940

Since you already started with the Selenium docs. I think it'll be in help too, if you take a look at these Test design considerations. As addition to @Master Slave answer, you'll see

Wrapping Selenium Calls

As with any programming, you will want to use utility functions to handle code that would otherwise be duplicated throughout your tests. One way to prevent this is to wrap frequently used selenium calls with functions or class methods of your own design.

and extend it with ‘Safe Operations’ for WebElements Presence.

Regarding the Page object, it's nice to read the tips for PageFactory too.

Upvotes: 1

Master Slave
Master Slave

Reputation: 28519

IMHO the key benefit is that the page objects and tests evolve independently. If there is a structure change, you don't change your tests, and vice-versa, if you want to change what you're testing, no need to change your page objects. Will try to illustrate this in an example

Think of the test written without adhereing to page objects pattern, e.g. testing product details.

this.driver(Find.By.id("username")).sendKeys(username);
this.driver(Find.By.id("pass")).sendKeys(password);
this.driver(Find.By.id("submit")).click();
// navigate to product listing page
this.driver(Find.By.id("product-usb")).click();
// navigate to product details page
this.driver(Find.By.id("product-a")).click();
assert(this and that);

In this example, if a structure change occurs (a major redesign) you're force to change your tests. Now, you're anticipating changes, and a first thing, you make sure that there's no code duplication so that you change on one place only when the time's right. You split the code in methods, and the most reasonable split is based on the logical units, while the asserts remain in the test where they belong

   login(username, password);
   moveToProductListing();
   selectProduct(productID);
   assert(this and that);

The question is where these methods should be implemented, so that you can access them easily from within the tests. If its one utility class, it will quickly become bloated. Thinking further will lead you to grouping by page objects.

You end up in your test not knowing anything about the structure of the pages being tested (its hidden in the page objects), and page objects not knowing anything about the test specific assertion you're making.

Upvotes: 1

Related Questions