
What is a page object Simply put, a page object is an object that Selenium uses as a representation of an HTML element. Selenium tests will not interact with HTML code directly, but with objects that use selectors to refer to particular bits of the HTML code.
Defining page objects
You will need to create independent classes for declaring your page objects. Such a class should group together all the page objects that belong to the same page, or to the same module that is about to be tested. They should be grouped logically and naturally. The tests and page objects should not belong to the same Java class (they should be independent one of the other). This has a great number of advantages, amongst them: avoiding redundant code (having a page object in only one place), availability of a page object to every test class that needs it (every test class that needs an object will access it from the same location), changing of the selector will be done in only one place if the HTML code changes.
A recommended way of declaring a page object is presented here:
@FindBy(how = How.HowValues, using ="value") private WebElement elementName;
Here, 'How' is an enum pertaining to the 'org.openqa.selenium.support' package, that can have one of the following values: CLASS_NAME, CSS, ID, ID_OR_NAME, LINK_TEXT, NAME, PARTIAL_LINK_TEXT, TAG_NAME, XPATH. The "value" value is an expression that defines a selector of the 'how' type. For example, if the how is 'CSS', the "value" parameter will need to define a CSS selector (as explained in this post: https://imalittletester.com/2014/02/25/css-selectors/). If the how is 'XPATH,' the "value" must be a valid XPATH selector (as explained in this post: https://imalittletester.com/2014/02/25/xpath-selectors/). If 'ID', 'NAME' or 'CLASS_NAME' are used, the actual value of the element's attribute that is represented by the 'how', will be declare as 'value' (for example, if 'how=How.ID', and the element has an id, the value of the id will be declared as the 'value'). The usage of the most important of these values is exemplified below.
Note that the webElement described above has the 'private' visibility attribute, which means it will not be accessible from within an outside class. This is a best practice. To access the value of the webElement from an outside class, you will need to declare a getter method for each webElement, and call the getter method, instead of the actual element. A getter simply returns the webElement, as shown below:
public WebElement getElementName() {
return elementName;
}
You can also obtain a list of webElements, if the selector you use to declare the webElement returns more than one results (if the expression used to get the "value" returns multiple results). In this case, you will define the list of webElements as follows:
@FindBy(how = How.HowValues, using = "value") private List<WebElement> listOfElements;
In this case, the getter will be changed to return a list instead of a webElement, as follows:
public List<WebElement> getListOfElements() {
return listOfElements;
}
Note that if the selector used to declare the list of webElements returns only one element, the list will have only one element.
Opposed to that, if the selector used to declare a single webElement returns more than one, the webElement returned will be the first encounter of an HTML element that complies with the specified selector.
XPATH
Example: If the XPATH selector to be used is //div<@class 'aClass'="'aClass'">/ul/li<3> (and it returns only a webElement), the declaration for it will be:
@FindBy(how = How.XPATH, using = "//div<@class 'aClass'="'aClass'">/ul/li<3>") private webElement element1;
Example: If the XPATH selector to be used is //div<@class 'aClass'="'aClass'">/ul/li (and it returns a list of webElements), the declaration for it will be:
@FindBy(how = How.XPATH, using = "//div<@class 'aClass'="'aClass'">/ul/li") private List<WebElement> listOfElements1;
CSS
Example: If the CSS selector to be used is .aClass li:nth-child(3) (and it returns only a webElement), the declaration will be:
@FindBy(how = How.CSS, using = ".aClass li:nth-child(3)") private WebElement element2;
Example: If the CSS selector to be used is .aClass li (and it returns a list of webElements), the declaration will be:
@FindBy(how = How.CSS, using = ".aClass li") private List<WebElement> listOfElements2;
ID
Example: If an HTML element has the id myId1 (and it is unique in the HTML file), the webElement declaration will be:
@FindBy(how = How.ID, using = "myId1") private WebElement element3;
CLASS_NAME
Example: If an HTML element has the class aClass (and there is only one element with that class name), the declaration will be:
@FindBy(how = How.CLASS_NAME, using = "aClass") private WebElement element4;
Example: If there are several elements that hves the class aClass, the declaration will be:
@FindBy(how = How.CLASS_NAME, using = "aClass") private List<WebElement> listOfElements4;
Running the Selenium test
After having declared all the needed page objects, the test class needs to be created. In this class, first the browser (driver) needs to be initialized (as described in this post: https://imalittletester.com/2014/02/27/run-tests-on-multiple-browsers/). For example
WebDriver webDriver = new FirefoxDriver();
Afterwards, you need to initialize the page object class, so that the elements declared in it can be used to interact with the page (here, 'webDriver' is the browser instance opened earlier; 'PageObject' is the class where the page object elements are declared):
pageObjectClass = PageFactory.initElements(webDriver, PageObjects.class);
This initialization is a bit more special. It calls the 'initElements' method of the webdriver specific PageFactory class, which declares a proxy to the page objects found in the class. The elements in the page objects class are initialized in a lazy fashion, which means that the compiler will look if the element exists on the page only when they will be used by someone (if someone calls a method on them, for instance if someone tries to click on the element). This means, naturally, that the elements declared in the page object class do not need to be present on the web page that is interacted with from tests, until the time they are used. As an example, by clicking on a button, another item appears on the page. This item is declared in the page object class, but is only used after the interaction with the button. Trying to interact with the item before clicking the button will throw an error, but the fact that is declared in the page object class is perfectly valid as long as the interaction with it is done properly (it will only be usable after someone clicks the button). To summarize: in the page object class you can declare elements that are not present on the page from the beginning, as their presence will be evaluated only when an interaction with them is performed.
After the page object class initialization, you can start writing the tests. In the tests, interaction with the web elements is done by using the appropriate Selenium methods (like click - to click on the element, getText - to retrieve the value of the text that the element contains, and so on).
pageObjectClass.getElement1().click();