What is page-object/ Page Object?
When we interact with web pages on a browser and have to interact with an element on the page, we find the element using selenium api (remember find elements section) and perform an operation on it. The concept of page object is to be able to have the element initialized by the time we want to interact with it, so that we don’t have to dig into the DOM every time we need to interact with it.
The concept looks awesome and one might think – well, then all I have to do is to create a class, have the html elements as its members and probably have some methods of the class as keywords [yes the same keywords we talked in keyword driven framework]. And yes, that is where we will start ! This section falls under the category of selenium framework
Modeling a page-object on synchronous applications is easier because the page-object represents the underlying html source of the web page. So for example, if the web page is fully loaded into the DOM, then our page-object [once initialized] will represent the web page and all we need to do is to call the members of the page-object and interact with it [vs. trying to locate the element before we interact with it]
However, some of us who already know this would question – well, that is not as easy on asynchronous applications [yes the ajax calls]. That is correct too ! Asynchronous calls have to be dealt with extreme care and we will talk about some strategies to have listeners [callbacks] that will make our life a tad bit easier. At the end of this block of sections on page-object frameworks, we will talk about the caveats, misconceptions and assumptions that most of us have around page-object frameworks and how “trappy” [not sure if that is a word] it is to think implementing a page-object framework that aligns with the our overall goal of framework is extremely easy [the goal being model the state and behavior of an application]
That said, lets go ahead start with some basics, implement a model and then refactor it and then talk about more exceptions to rules right 😉
The agenda topic span across multiple pages, but will remain under the section page object framework in the menu on the left. So please navigate to the next page once you reach the bottom of this page.
- Define a page-object
- Define page factory methods
- A concrete workflow
- Modeling page-objects
- Implement page factory pattern
- Dealing with asynchronous web applications
- Caveats and closing thoughts
Coming back to our discussion on page-object, let’s say I want to represent the home page for seleniumframework website. The page looks as below:
The email link in the top bar has an html as shown below. It has class=”email”
Similarly in the above picture, we have address element which has class=”address”.
Similarly in the above picture, we have phone element which has class=”address”.
seleniumframework home page-object:
So if we go ahead and represent this as a class, it might look something as below.
This represents the page-object for seleniumframework home page.
- We create a class
- The constructor (__init__ method) initializes the elements
- The getter methods return the corresponding elements
def __init__(self, context):
self.address = context.browser.find_element(By.CLASS_NAME,'address')
self.phone = context.browser.find_element(By.CLASS_NAME,'phone')
self.email = context.browser.find_element(By.CLASS_NAME,'email')
Obviously this is not the most mature way to represent the page-object, however I believe you get the idea here.
So how do we consume a page-object. An extremely simple way is to instantiate the page-object in the calling code and access the elements, for example:
Then(/^I print home page elements text$/) do
home_page = AutomationHomePage(context)
So we have seen how to implement an extremely easy page-object pattern here where we represented the home page. This way, we can go ahead and represent the web pages with page-objects and use them in step definitions and interact with them. Is it so simple ?
Well, the understanding of the pattern is pretty much what we discussed here, there are of course more nuances such as the following:
- Do I initialize the elements in the constructor always ?
- How many times do I initialize the page-objects if it is being used in multiple step definitions [calling code]
- Do I have to create getter/setter methods for all the elements of the page-object. Seems like lot of duplicated code. Can’t I abstract it out ?
- We know that there are some commonalities across all web pages – For example , the context.browser object [selenium object] applies to all web pages. Can I abstract it out ? Can I declare a base page? Can all my web pages inherit from base page ?
All of the above questions are valid and there are more. Let’s take it step by step and by the end this tutorial on page-object framework, I am sure you will appreciate some patterns we talk about here.