2

So this is sort of a follow up question to me previous question: Page Object Model: How much abstraction is necessary?

In an effort to make my test scripts simpler and methods easier (since implementing a very discrete PoM didn't really reduce my test scripts) i've came into the problem of how to handle methods that handle complex forms for example.

With a page object model something like a Login()method is pretty easy, it takes a username and password. So even if we have to pass parameters in the test script it's not a big deal.

But for example I have a page that deals with creating a widget for example (That's this particular pages sole purpose). The form is a series of input text fields, dropdowns, etc... (This framework is written in Ruby by the way).

How do I go about dealing with this with PoM?

Currently i've made a method for each input (along with the "save" button)...but that doesn't really abstract away anything, and honestly is just as long in the test script as using pure selenium calls.

I could make a method that handles all this (Which makes sense) but the number of parameters I would need to pass would be staggering. OR I could just use default parameters (perhaps using a gem such as faker to fill in inputs within the method). Or perhaps have another class (which seems overkill) or fixture to pass into the method.

I've seen some guides that sort of have a default data of {} that uses default in the method by merging the defaults into a blank object (if nothing is otherwise passed). Which seems like an ok way of handling it.

What's the best way to handle this?

  • In these scenarios, I have successfully used complex data objects({} Json objects) to pass around as a single parameter between page object methods and defined single big methods to fill in all page UI objects like 'fillOrderDetails' instead of single element handling functions. – Vishal Aggarwal Aug 14 at 23:02
  • This also make a lot easier to compare all data values between 2 pages(fill order details & confirm order details) as a simple 2 object comparison function. – Vishal Aggarwal Aug 14 at 23:08
  • @VishalAggarwal this seems like the way to go, as having discrete functions for every input just made the test script super long – Mercfh Aug 15 at 2:42
2

A check usually follows the Setup-Act-Check pattern.

In the setup, you put the environment in the minimal state relevant for your check, including your test data.

Let's consider a form that have 3 sections and a few fields on each.

Your test could be some of this sort:

FormPage form = FormPage()
FormData data = FormData("some_test_data_file.json")

form.fill(data)

assert.equals(form.section1(), data.section1)
assert.equals(form.section2(), data.section2)
assert.equals(form.section3(), data.section3)

Your page object would be also straightforward:

FormPage
....
fun fill(FormData data){
    section1.fill(data.section1)
    section2.fill(data.section2)
    section3.fill(data.section3)
}

....

Section1

fun fill(Section1Data data) {
    name.input(data.name)
    name.input(data.age)
    overage.check(data.isOverAge)
    ....
}

And the test data object is basically composition:

FormData

Section1Data section1
Section2Data section2
...

Section1Data

String name
String age
Boolean isOverAge
....

Every single method is follow the Clean Code recommendations:

  • Should be as small as possible -> One to three lines here
  • Should have an explicit intent, both in its name and code -> No loops or ifs here
  • Should do one thing only and do it well -> Delegate as much as possible to lower level functions

The magic here is in creating almost a mirror of your test data structure and the page object, which allows you to:

1 - Simply create both at test and ask them to deal with each other;

2 - Know almost always the points on the test data and page object you have to change to adapt to a change in the entity the they represent - if you have a new section, just add something to FormPage.fill and a new attribute to FormData | if age is not necessary, remove the attribute and change Section1.fill;

3 - Almost no change in the UI will make you have to change the test itself;

With this basic setup, you can come up with optimizing ideas of DefaultFormData, RandomFormData, FormDataSection1 (which takes data from a file for section 1 and randomizes the rest), etc.

Note: Even for the login example, I would argue for creating a LoginData object - username and password at this moment represent a single entity, and they don't make sense by themselves.

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.