23

When you are modelling your page objects, how would you deal with a page which has form and about 50 input fields on it? What is the best practice here?

Would you create a page object and write a separate function for each input action? or would you write one function which parameters are passed to it and enters the text?

e.g.

public void enterFirstName(String firstName) {
    driver.type("firstNameField", firstName);
}

public void enterSecondName(String secondName) {
    driver.type("secondNameField", secondName);
}

or

public void fillInForm(String inputFieldName, String text) {
    driver.type(inputFieldName, text);
}

I can see in the first model, when writing tests, the tests are more descriptive, but if the page contains too many input fields, creating the page object becomes cumbersome.

This post is also quite interesting in structuring selenium tests in Page Objects Functional Automated Testing Best Practices with Selenium WebDriver

Nat Ritmeyer
  • 5,634
  • 8
  • 45
  • 58
Amir Ghahrai
  • 618
  • 1
  • 7
  • 22
  • Take a look at [my stackoverflow question](http://stackoverflow.com/questions/8149808/whats-the-best-way-to-use-selenium-pageobject-design-pattern) for an example on how I'm going to be using the page object design pattern. I'm not 100% myself but from a lot of reading, I'm sure im on the right lines, hope what I have pasted helps you. – Patrick Magee Nov 16 '11 at 22:31
  • http://selenium-tutorial.blogspot.com/2012/06/webdriver-page-objects-pattern.html – tharani dharan Jun 27 '12 at 11:50
  • 1
    You could put all similar WebElement types into a List. so, if you have 40 text fields you can draw from the list one by one, get the "name" or "id" to identify it, and then operate on it. – djangofan Oct 17 '12 at 06:25
  • Follow single responsibility principle while designing your page objects - You will have a well readable and reliable tests - more info: http://www.testautomationguru.com/arquillian-graphene-page-fragments/ – vins Sep 20 '16 at 03:19

6 Answers6

10

The idea behind the page object model is that it abstracts the implementation away from the caller. In the first mechanism, you are successfully doing that because the caller doesn't need to know if the html input field name changes from "firstName" to "user_first_name", whereas in your second implementation any changes to the actual page would have to be trickled out to all callers of your page object.

While it may be more work up front to create your page object, if you maintain the encapsulation it'll save work in the long run when the real html page inevitably changes.

digitaljoel
  • 26,265
  • 15
  • 89
  • 115
  • Thanks digitalJoel, How about creating the page with public locator variables (the how) defined at the top of the page and then in my Test class, I instantiate that page and I pass in the locator variables to the generic fillInform. In that sense, still my variables are in one location, the page object, and all my tests will make reference to those variables, so when they do change, I still have to maintain in one location. – Amir Ghahrai Nov 03 '11 at 16:58
  • That would be one way to do it. Having the names as public static final fields and then supplying the one method. That works until you need to do some special processing for a single value, then you have a separate method for `myPage.setSpecialValue( blah )` and for everything else you have `myPage.fillInForm(MyPage.MY_NAME, blah )` and it's inconsistent, but that may be a bridge you can cross when/if you come to it instead of having to build for it now. It's up to you. – digitaljoel Nov 03 '11 at 17:10
  • 2
    The best "thanks" would be to up-vote some answers. Out of 14 answers to your questions you have only up-voted 4 answers and not accepted any answers. I'm not talking about just my answer, but any answers that you find helpful (like the one by Sam Woods on this question) can/should be up-voted. – digitaljoel Nov 03 '11 at 19:56
9

I always like to break things up into groups of related information. For instance, if I have a user class I might break that up into a few smaller classes: LoginCredentials, ProfileInfo, Settings, etc, but I would still usually have a top level User class that contains these sub classes.

One thing I would certainly recommend would be to pass in an object to one FillForm function rather than all of those individual functions. There are some great advantages using this approach. One, you could have some "common" pre-configured objects that you use for many of your test cases. For instance:

public class FormInfo
{
   string Domain;
   string Name;
   string Category;
   // etc...

  public FormInfo(string domain, string name, string category)
  {
     Domain = domain;
     Name = name;
     Category = category;
     // etc...
  }
}


// Somewhere in your initialization code
public static FormInfo Info1 = new FormInfo("myDomain1", "myName1", "myCategory1");
public static FormInfo Info2 = new FormInfo("myDomain2", "myName2", "myCategory2");

You can still update one of your common merchants if you need to do something one-off:

// In your test case:
Info1.Category = "blah";
FormPage.FillForm(Info1);

OR, you can create a brand new merchant object for a specific test case if necessary. You can also do things like field validation either using these objects, or what I normally do is break the page object pattern for specific field validation, so if I am validating the merchant domain field I might do this:

Info1.Domain = null; //This should make the FillForm function skip doing anything with this field.
FormPage.FillForm(Info1);
FormPage.DomainTextBox.Text = "field validation string";

Another important advantage of this approach is that if the page is ever updated to add, remove or modify fields, you would only need to update your FormInfo object and FillForm function, and would not need to modify specific test cases that call the FillForm function - assuming they are using one of your common FormInfo objects. Another possibility to get more coverage would be to set up one of your common FormInfo objects to generate random strings for each of the fields that comply to the min/max length and cycle between all different allowed characters. This allows you to get some additional testing out of the same set of tests, although it could also add some noise if you start getting failure results only from specific strings, so be careful.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Sam Woods
  • 1,820
  • 14
  • 13
1

I am answering an old question for the benefit of readers.

Along with other good answers here, I would like to add few suggestions here for those who are new to POM.

Page objects is a well known design pattern, widely accepted by the automation engineers, to create separate class file for each page of the application to group all the elements as properties and their behaviors / business functionalities as methods of the class. But it has few issues in creating a class for a page - especially when the page has more / different sets of elements / complex element like a grid / calendar widget / a HTML table etc.

The class might contain too many responsibilities to handle. It should be restructured and broken into smaller classes. Ie, following the Single Responsibility Responsible.

Check the image here for the idea.

enter image description here

That is, create reusable page fragments & let the main page object serve the page fragments.

Check here for more info.

vins
  • 15,030
  • 3
  • 36
  • 47
1

In addition to your enterWhatever() methods, I usually also create a createWhatever(field1, field2, ...) method, which I can use as a fast path to creating whatever the form builds, for use when the real purpose of the test is something else. Thus, if I need to create a Customer in order to test submitting a Ticket, the test goes to the CreateACustomer page and just invokes createCustomer(firstName, lastName, emailAddress, ...), and then continues on to the more-nuanced task of creating a Ticket using that Customer.

Ross Patterson
  • 9,527
  • 33
  • 48
0

Please have a look at the readme, https://github.com/yujunliang/seleniumcapsules

0

The way I do this in my forms is to get a list of all of the inputs on the page. Then remove any input elements that are not displayed. After that I can put valid or invalid text to each of the inputs. From there I catch the validation summary to make sure I am getting the correct error or not. If not then log exception.

What this does is allow me to input text into as many inputs as are on the page, and it still allows me to log exceptions and send them via e-mail. I also catch textareas and password fields in my list and I have a separate list for checkbox fields and Options since there are different things that I usually want to do with those.

what it boils down to is that all I have to do to test a page is this:

for (int i = 0; i < inputs.Count(); i++)
{
  //This captures the error message string created in the input validation method
  //nextButton is the IWebElement of the button to click to submit the form for validation
  //ErrorMessageID is the ID of the Validation Summary display box (i.e. ErrorMessageID = "FormSummary" <asp:ValidationSummary ID="FormSummary" runat="server" CssClass="errorMessage" />

  string InputValidationText = utilities.InputValidation(driver, inputs, i, nextButton, ErrorMessageID)
  if(InputValidationText != string.Empty)
  {
    //LogError
  }
}
CBRRacer
  • 4,649
  • 1
  • 23
  • 27