Reputation: 1186
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
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:
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
Motivation
Upvotes: 0
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
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