Page Object Model Multi-Layer Structure:
1. First layer– consists of the driver layer which could be either Appium or Selenium and the implementation of methods and logic we don’t change often (such as cross browser support, reporting module, data driven implementation).
2. Second layer- can be a library code that is built on top of the driver layer (Appium or Selenium), good examples for such are: Robot framework and Selenide. These layers are used to hide any complex actions as waits, page elements validations and more.
3. Third layer- the third layer could be used by the test automation developer for any complexes action that can come on top of the first or second layer. For example, after utilizing the wait function and validation in the second layer, we’ll be able to add several other actions such as calls to externals libraries or any other specific logic.
4. Fourth layer- we’ll be able to easily call actions we established in the third layer creating functional test cases and will eventually become our testing layer.
Next, I would like to demonstrate a basic example of page abstraction using page object model in C#:
First, we’ll create a class named Page and connect a builder to it which will receive the driver and the time frame which will be the upper limit for all ‘waits’ in our test implementation.
- public class Page
- {
- private IWebDriver driver;
- private TimeSpan defaultTimeSpan;
-
- public Page(IWebDriver driver, TimeSpan defaultTimeSpan)
- {
- this.driver = driver;
- this.defaultTimeSpan = defaultTimeSpan;
- }
- }
Now, we will implement the locators which are the methods for identification, and the same object that Selenium uses to identify elements. Overall, we’ll have 8 different types of locators (read more about determining element locators).
- By.Id
- By.Name
- By.ClassName
- By.TagName
- By.LinkText
- By.PartialLinkText
- By.CssSelector
- By.XPath
‘By’ is the object also called the locator and the implementation will look as follows:
- private By ByLocator(string element, Locator locator)
- {
- switch (locator)
- {
- case Locator.Id:
- return By.Id(element);
- case Locator.Name:
- return By.Name(element);
- case Locator.TagName:
- return By.TagName(element);
- case Locator.ClassName:
- return By.ClassName(element);
- case Locator.LinkText:
- return By.LinkText(element);
- case Locator.PartialLinkText:
- return By.TagName(element);
- case Locator.XPath:
- return By.XPath(element);
- case Locator.CssSelector:
- return By.CssSelector(element);
- default:
- throw new Exception("No Locator Found");
- }
Next we’ll implement the ‘Find’ method which will be the base for several locations later on (using the ByLocator method we created earlier):
- public IWebElement Find(string element, Locator locator)
- {
- return driver.FindElement(ByLocator(element, locator));
- }
We will continue implementing the rest of the methods (while utilizing the Find method)
- public Page Fill(string element, string value, Locator locator)
- {
- Find(element, locator).Clear();
- Find(element, locator).SendKeys(value);
- return this;
- }
- public Page WaitUntil(Func<IWebDriver, TResult> condiction)
- {
- new WebDriverWait(driver, this.defaultTimeSpan).Until(condiction);
- return this;
- }
- public Page NavigateTo(string url, string proofElement, Locator locator)
- {
- driver.Navigate().GoToUrl(url);
- new WebDriverWait(driver, this.defaultTimeSpan)
- .Until(d => d.FindElement(ByLocator(proofElement, locator)));
-
- return this;
- }
In the top layer, we will initialize the methods same as ‘Fill’, while sending the required arguments (these arguments come from the same tester as a part of the automated test). Nonetheless, the test automation engineer which creates the test case scenario, is unfamiliar with the complexity of the Fill method which itself uses other methods.
Please note: the example presented above, does not represent a classic case of an Abstract Class in which we are declaring methods and making sure to implement in inherited classes, but another method to use abstraction over pages.