Skip to content

PageObjects

Konstantin edited this page Nov 8, 2016 · 101 revisions

An introduction to PageObjects.

Table of content

  1. [What is PageObject] (https://github.com/takinosaji/TestUnium/wiki/PageObjects#what-is-pageobject)
  2. [Anatomy of PageObject] (https://github.com/takinosaji/TestUnium/wiki/PageObjects#anatomy-of-pageobject)
  3. [Instantiation of PageObjects] (https://github.com/takinosaji/TestUnium/wiki/PageObjects#instantiation-of-pageobjects)

What is PageObject

PageObject - is one of the core concepts of TestUnium.Selenium module of our framework. It is an abstraction, which is represented by a class, which plays role for the container of the HTML elements for certain webpage or it's part. Becides it main contain additional logic to manipulate with this elements.

Anatomy of PageObject

In order to create PageObject you need to define a new class (don't forget to give semantically correct and understandable names for your classes :) ), which must be derived from PageObject class. This class gives you an access to:

  • Settings of your test Context
  • Current IWebDriver instance of your test context
  • Predefined WebDriverWaits (Small, Medium, Long) bound to current IWebDriver

Have a look at the example of simple PageObject:

//[Lazy] //optional
//[Name("GitHub main")] //optional    
[Marker(How = How.XPath, Pattern = "//*[text() = 'Sign up for GitHub']")]
[Marker(How = How.XPath, Pattern = "//a[text() = 'Sign in']")]
public class GitHubMainPage : PageObject
{
   public String LoginName;

   [FindsBy(How = How.XPath, Using = "//*[text() = 'Sign up for GitHub']")]
   public IWebElement SignUpBtn { get; set; }

   public IWebElement StickySignUpBtn()
   {
        return Driver.FindStickyElement(By.XPath("//*[text() = 'Sign up for GitHub']"));
   }
}

As you see on the example we have created simple PageObject class to describe GitHub main page.

In order to access HTML-elements of a web page within your code you can use several approaches.

  • First and the most convenient one is about using properties, decorated with FindsBy attribute (SignUpBtn property in our example). In this case these properties will be initialized just after checking of page Markers during the creation of the page.
  • The second approache is to define methods which returns IWebElements. Inside this method you can instantiate IWebElement manualy, perform any cheks and than return that element (StickySignUpBtn method in our example). At least it is up to you how to provide an access to web elements, but you should keep in mind this features together with the flow of creation of the instance of PageObject.

Markers

Markers is a special mechanism that allows us to check that desired web page is actually displayed in the browser. As a part of PageObject class it is defined as a collection as an IWebElements. You may specify as many IWebElements as markers as you wish, but at least one.

There are several ways to define Markers for PageObject.

  • The first way - by using [MarkerAttribute] (https://github.com/takinosaji/TestUnium/wiki/MarkerAttribute). For specifying multiple Markers use multiple attributes. By this way we define Markers for any instance of PageObject class if others are not passed as a parameter in IWebDriver.GetPage method. Think about this way as like setting Markers for a template. This way is satisfying and reflecting the purpose of using Markers and is acceptable in most situations while you are working with culture and country independent web pages or it's parts.
  • The second approach is to pass required Marker selectors into markerSelectors parameter of one of IWebDriver.GetPage extension methods. In this case all Markers defined by attributes are ignored. This way opens an opportunity to have different Markers per instance of single PageObject class. This may seem frustrating on a first glance, because, as we prompted, Markers are unique signs of a web page. But if we will think about multi-language and country localized web pages, it comes clear that the same semantic HTML elements on a page may have different content (attributes, text) . For instance the same "main page" of some "www.super-site.com" for Russian Federation (/ru) will have different title or text of paragraphs, headers, etc. from Polish (/pl) version. And all this unique culture related elements could serve as Markers in our code.

Lazy loading

Usually we are checking presence of Markers and load web elements at the same moment of creation of an instance of PageObject class. But if we don't want to do this we can use Lazy loading. [Lazy Attribute] (https://github.com/takinosaji/TestUnium/wiki/LazyAttribute) allows us to create an instance of the PageObject, without checking for presence of elements defined as Markers. Just decorate your class with it like it shown in example:

    [Lazy]   
    public class SomePage: PageObject
    {
       //Content
    }

Names

Instantiation of PageObjects

The page itself is created by using several extension methods of our Framework, defined in [SeleniumExtensions.cs] (https://github.com/takinosaji/TestUnium/blob/develop/src/TestUnium.Selenium/Extensions/SeleniumExtensions.cs):

public TPageObject GetPage<TPageObject>(Action<TPageObject> pageTransformAction = null, Boolean supressLoad = false, params By[] markerSelectors);         
public TPageObject GetPage<TPageObject>(Boolean supressLoad);
public TPageObject GetPage<TPageObject>(params By[] markerSelectors);      
public TPageObject GetPage<TPageObject>(Boolean supressLoad, params By[] markerSelectors);           
public TPageObject GetPage<TPageObject>(Action<TPageObject> pageTransformAction, params By[] markerSelectors);            

In our example:

var gitHubPage = Driver.GetPage<GitHubMainPage>();

In order to pass different data, that can be used in the certain pageObject instance you can use lambda expressions. Just create needed fields in your PageObject class before (LoginName field in our example):

var gitHubPage = Driver.GetPage<GitHubMainPage>(s => { s.LoginName = "LoginName"; });