Accessors and Page Objects in Automated Testing
There are many ways to formulate an automated test for a specific page layout. One such way is to use vaguely constructed, yet universal methods to interact with elements and objects on the page. For example, say you want to have a universal click command for buttons:
This definitely has its benefits, as you can use it on any button that is currently displayed in the page layout. The difficulty lies when the page layout is changed and that identifier is modified, which is often the case in a fast moving agile environment. Not only that, but sometimes dynamically generated elements are difficult to address since the dynamic nature of the element can many times go against the nature of a ‘unique_id’.
One such solution to this problem is by looking at each unique page as its own object. Take a look at this simple, crude example page layout:
To access the ‘Login’ button:
Same for Password and Email:
browser.text_field(:id, ‘Email’).set(‘My Value’)
But what if beyond the accessor unique ID (text, id) changing, what if the actual element type itself changed? What if Login was no longer a button and was now a link? Your universal click link would no longer work for this button.
A common and elegant solution to this problem is to create a page object for Example_Home_Page.html. Each element contained within this page has its own set of methods (accessors, getters, setters) contained with in the Example_Home_Page.object. This solution allows you to quickly and easily find and modify or update tests for a specific page.
The designer who decided to change my Login button to a Login link wouldn’t pose as much of a problem, as the solution is simple:
def login_to_app(email, password)
#browser.button(:id, ‘Login’).click #THE OLD ACCESSOR
In this class, you can easily add, remove and modify methods for each element of the page in a very readable format. Next, let’s see how it’s easier to deal with the dynamic content. Lots of times, dynamic content can be the most difficult content in which to construct test coverage. This is especially true when there is a disconnect between the designer/developer and automation engineer. A common (and easy example for this…example) way to do this is using xpath. Let’s construct a simple Cucumber test:
Scenario: I want to get the headlines of the newest item in the news feed.
When I get the newest headline of the newest item in the news feed
Then I successfully get the newest item in the news feed
Now, let’s modify our Example_Home_Page object to house our new method:
def login_to_app(email, password)
This new method, get_newest_headline, will always check the top element which just happens to ALWAYS have this xpath address. Because of that, you don’t have to worry about unique IDs that must remain the same (minus the xpath, which allows the content to be dynamic). Additionally, if the xpath changes in design, just simply change it in your page object.
To summarize, there are a number of solutions in which to interact with elements on a page and many of these solutions can be tailored to fit any project. In my case and experience, it has always been easier to keep your tests organized as page objects so that maintaining them is as simple as possible. And simple is usually better.
About the Author
Stephen Atkinson, Distil's QA Automation Engineer, started his software engineering career as a developer for the East Carolina University, where he took part in research and development of video games that would be used in a medical environment for diagnosis and rehabilitation. From there, he expanded his knowledge into automated software testing at iContact and later, 6fusion.More Content by Stephen Atkinson