5

So i've been researching the Page Object Model when composing an automation framework. Most examples are done in Java, but since im using Capybara with Selenium Webdriver (Ruby) for Rails, I imagine there is no reason I can't use POM with that as well.

However most examples of POM I see are simple stuff, like logging in/etc..., the problem I have is that the Web Application im working on developing a framework for isn't so straightforward.

When I think of POM and from what I can tell, each "Page" is a separate class. However in this application, Creating a "Widget" we'll call it, is a 7-8 step process. Sometimes the Page forms can be 15+ Items. So that's where my confusion comes in and the following questions:

  1. Should I still make each step/page a Class?
  2. In a page with a ton of forms, should each "Input" be a definition/method...or should I initialize the class with all the inputs defined in the constructor?
  3. Since the pages are mostly forms + Submit button....what should the methods in the class be?
  4. Since a "Script" of creating the widget would be 7-8 different steps...would I just be creating 7-8 different POM objects in the script? This seems like it doesn't make sense. HOWEVER I can see where if someone just wanted to test 1 part it would make sense (So I guess it does make it modular).
2

One option for some some sites is to have, or at least start out with one Page Object Repository and avoid complexity that way. Especially if you use highly descriptive names, for instance if you use 'click_submit' as a name that is too general too have multiple different ones. But if you use 'click_submit_initial_loan_application', 'click_submit_add_cosigner_application' and 'click_submit_add_personal_contact' then having them all in one file will be ok.
I have used this model at several places with ruby-rspec-capybara

Currently I used Page Objects as follows:

Create a Page Object yaml file for the key-value pairs of name:selector, e.g. (sample)

# spec/support/page_objects.yml
...
student_dob_day: 'STU_ST_DOB2'
student_dob_month: 'STU_ST_DOB1'
student_dob_year: 'STU_ST_DOB3'
dialog_frame: 'iframe#dialogIFrame'
how_to_apply: 'BO_HowToApplyChoice'
how_to_apply_individual: 'BO_HowToApplyChoice[value="I"]'
electronic_consent: 'img#ElectronicConsentAccept'
...

Then I create a way to load them up into a Page Object by using the following meta-programming to dynamically create methods:

# spec/support/page_object.rb
class PageObject

  def initialize
    load_page_object_file('spec/support/page_objects.yml')
  end 

  private

  def load_page_object_file(file)

    page_object = YAML.load(File.read(file))
    page_object.each do |k, v|
      self.class.__send__ :define_method, k do v end 
    end 
  end 

end

The above literally creates method names for the page objects names with return (key) values being the selector.

Then, within a spec that I want to use Page Objects in I do:

describe "some test" do
    p = PageObject.new

then within the test itself I use the Page Objects like this:

...
select 'JAN', from: p.dob_month
fill_in p.dob_day, with: '01'
fill_in p.dob_year, with: '1978'
...

At a different level, which you may also be referring to, I create custom named methods to also abstract away the details and end up with a DSL like the following:

fill_out_school(p, 'COLUMBIA UNIVERSITY')
fill_out_first_degree_major_enrollment_status_dropdowns(p)
fill_out_first_grade_level(p)
fill_out_years(p, this_year)

with these methods having the actual capybara webdriver find and click and type commands. I pass in the PageObject already created.

I don't normally like abbreviations but as 'page' will occurs hundreds or thousands of times I reduce it to "p" as it is just so obvious what it is. 'guess it's the rubyist minimalist in me!

btw I've also used JSON and XML as the storage mechanism and this has certainly taught me that Page Objects are an approach that can have several implementations.

  • Nice clean way to define objects! – Vishal Aggarwal May 29 '18 at 19:50
  • @MichaelDurrant Curious on your examples like fill_out_school why are you passing in the Page Object? Inside of the method are you doing something like fill_in p.student_school or something? – Mercfh Jun 11 '18 at 17:17
1

Have you explored SitePrism?

SitePrism gives you a simple, clean and semantic DSL for describing your site using the Page Object Model pattern, for use with Capybara in automated acceptance testing.

  • I've seen a few gems that do something similar, but im trying to roll my own for practice at least. – Mercfh May 29 '18 at 17:42
  • It will just provide you an DSL , you still need to define your objects and methods. – Vishal Aggarwal May 29 '18 at 19:47
1

The POM is a model of the web application, so, if you have a complex applicaiton, the model will naturally reflect that. I have been involved in the testing of web applications with many page objects (tens if not more), with large forms, and with many steps required to perform a test - it can be challenging but is possible. My examples are based on Java, but should be applicable to any OO language.

Should I still make each step/page a Class?

I would say yes, you should make each page a class. This generally helps with maintainability and readability. Protip: If you aren't considering it already, then consider using usecases, as a layer between the step definitions, and the page objects.

The page objects may have methods like enterUsername(), enterPassword(), clickSubmit(). A usecase called CreateUserUC may have methods like createUser() which invokes the page object methods previously mentioned. And, finally, your step definition will call createUserUC.createUser(name, pass). That's not the best example but I've seen it help with reusability of methods especially where there are lots of steps, and forms which appear at various stages of the workflows.

In a page with a ton of forms, should each "Input" be a definition/method?

I would also say yes, have a setter for each input. Again, this helps with maintainability. However, please consider the following example form.

  • List item
  • firstName
  • middleName
  • lastName
  • addressLine1
  • addressLine2
  • addressLine3

Here we can easily abstract out a name object and an address object. In this case, I would create classes for both of those objects, and my Page Object would contain the following methods:

  • public void enterName(Name name)
  • public void enterAddress(Address address)
  • private void enterFirstName(String firstName)
  • private void enterMiddleName(String middleName)
  • private void enterLastName(String lastName)
  • private void enterAddressLine1(String addressLine1)
  • private void enterAddressLine2(String addressLine2)
  • private void enterAddressLine3(String addressLine3)

The first 2 methods would invoke the private methods. People might call this overkill, but in my experience it's safer to be overkill than to have insufficient structure and insufficient flexibility for change.

or should I initialize the class with all the inputs defined in the constructor?

I would certainly not recommend this approach, having said that I can understand the appeal. You can hide the details of setting all the fields within a usecase method. Typically I believe that the constructor for a page object should be used to set up the model of the page, if anything needs to be done, and that any interactions with the page (like filling in a form) should be done by calling methods after construction. This helps with maintainability.

Since the pages are mostly forms + Submit button....what should the methods in the class be?

This is standard for web applications. Methods of the page object should be along the lines of:

  • public void enterFirstName(String firstName)
  • public void enterName(Name name)
  • public String getErrorMessage()
  • public void checkSuccessMessagePresent()
  • public void clickSubmit()

Since a "Script" of creating the widget would be 7-8 different steps...would I just be creating 7-8 different POM objects in the script?

Yes. I'd say the above examples should help so I will just summarize with:

  • Consider usecases. They should be high level enough that they are succinct but also low level enough so you can reuse them and that they provide value
  • Consider abstracting out objects entered in the forms, and try to operate more with those instead (Address, Name) of the fine detail of every form (firstName, etc.).
  • Look for common items in the web application, perhaps certain types of forms are reused. You can have a partial page object, and then a page object may contain several partial page objects.
  • Keep in mind the web application could change regularly so try to write clean code, having each method do just one thing.
  • "Typically I believe that the constructor for a page object should be used to set up the model of the page". Sorry what would this entail? Given most of my pages are just forms? I guess if we have any constants we need to refer to or something? – Mercfh May 30 '18 at 14:11
  • 1
    It would differ depending on how you structure your project. As an example, you might have a parent page object, which has instance fields such as webDriver, baseUrl, menu... Therefore, within a page object class which extends the parent page object, you may do things like set the webDriver, set the baseUrl, menu, and so on. More configuration type things to set the page object up for interaction. The interaction itself can be captured in the methods outlined above. At least, this is the most common structure I've seen in my experience. – DtotheK May 31 '18 at 10:15
  • One thing i've seen difference on is methods for inputs. I've heard some people say it's better for the method to just return the element (then I do whatever I need to do with it), and then others say the method should actually do the .clicking or inputting and THEN return the element. Any thoughts on this? I can see where returning just the element almost makes more sense...in case you need to do assertions on it. – Mercfh Jun 11 '18 at 17:54
  • I suppose if you return the element from .click() then you can do something like: element.click().click().rightClick() which might make things concise. However I think returning void makes more sense and closer represents the reality - if you are trying to model the web page. – DtotheK Jun 13 '18 at 8:41
  • So your saying do the actual clicking/etc.. in the method, and pass any parameters that would be needed? – Mercfh Jun 13 '18 at 13:26

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.