The simplicity thesis
The simplicity thesis
My thoughts on automation engineering
The pageObjects pattern
• The idea of modeling the areas of the web UI that tests interact with, is a old but a very known pattern. It was discussed for the first time in the Google London Testing Automated Conference in 2006 where it was presented as „Web driven pattern“. The idea was like a illumination in the thoughts of a lot of automation engineers and it was officially introduced as a component from the Selenium project in the first Selenium Conference in 2011 in San Francisco. This approach became more and more popular and it is today one of the most recommended patterns to be implemented when writing automated integration tests using WebDriver.
• But there is still a problem with this practice, that should be considered: During my conversations with people in different conferences and workshops, I could observe, that some people don‘t exactly know why they are using pageObjects or why they should. They know that is cool, but why? What are the real consequences? And what is about the liabilities?
Why pageObjects?
• In my opinion the user interface is something that - as every natural process - is always evolving. I see it as a natural process because software and it‘s components, specially in early stages, tends to be changed and adapted continuously. In the most cases the UI will never looks like it has been planed at the beginning of the project. In the meanings of agile, that is something pretty common and also valid under some circumstances.
• The problem: Tests that hit the UI are brittle and cost therefore high maintenance.
• Another problem is that UI tests are mostly written in a way that just the automation engineer understands.
• And yet UI tests are very valuable, since they describe the behavior of a system providing examples from the end users point of view.
• These are problems that we, automation engineers, unfortunately have to deal with. We need to assure that changes in the user interface can be adapted in a easily way and that tests can be easily understood.
How do I implement pageObjects
• When using the pageObjects pattern to model user interfaces I try to decouple changes in the application under test from my test data, by implementing what is called protected variation layer. I start creating a class with appropriate methods for each web page or web page area, which I call module. The methods for the class should precisely reflect the interaction operations or „services“ for that pages on a business level. In a next step I describe appropriate fields for each element that the user is interacting with. I prefer to declare these fields as private elements that can only be accessed by the class through the public methods.
Here is how it looks like when implementing pageObjects using the pageObjects gem from Cheezy
class LoginPage
include PageObject
page_url '#{Efs::LoginUrl}'
text_field(:account, :name => '__opst_admin_account')
text_field(:password, :name => '__opst_admin_passwd')
button(:login, :name => 'submit')
def do_login_with(account, password)
self.account = account
self.password = password
login
end
...
end
Liabilities
• It is necessary to put a lot of effort into designing the pageObjects hierarchy to reflect the flow through the application. When the flow changes, it is necessary to change the connecting code between the textual representation of the tests and the pageObjects hierarchy. And this activity can be very time consuming.
• As I have said at the beginning, if the pages change in format or content, the pageObjects will have to change most likely as well since the pageObjects hierarchy is tightly coupled to how the web pages are designed. This is a point that should be taken in consideration when deciding the best moment to implement web automated tests.
Antipatterns
There are some things that I particularly consider antipatterns when working with pageObjects
• Hard coding urls on page classes (I would use YAML or some kind of mapping for that)
• Storing pages as instance variables (pages should be accessed directly)
• Making assertions in the page classes (page classes are independent from test context)
• Checking the whole page to match a single string (modules should be modeled for a specific page area)
• Representing the entire page in a single page class
• And finally I consider the biggest feature of pageObjects, where methods return other page objects, as a antipattern. I don‘t like to return page objects from methods since that means less flexibility to me. I understand a page as a standalone object that basically just exists and knows nothing about the world out there. With that, pages can be called in any order and navigate to anywhere. That why I prefer to create page objects on demand using for example the PageFactory from Cheezy.