logo logo

Page Object Pattern Advantages & Implementation

main post image

The following tutorial is the next step in Learning how to create a test automation framework with C#, Selenium 3 and Nunit : this step is about Page Object Pattern (also known as Page Object Design Pattern or Page Object) and the reasons to implement it when creating your test automation framework.


Tutorial Overview:

Class 1 – Creating an Automated Test Using Selenium WebDriver 3 and C#

Class 2 – How to Create a Test Automation Framework Architecture with Selenium WebDriver 3

Class 3 – Utilizing Test Automation Framework with Advanced Capabilities


 Page Object Pattern

To this point, we were writing code with no actual structure, focusing only on a single page and its elements and sending commands to Selenium. However, what will happen when working with multiple pages, as most likely in an actual automation case?

We will prefer not to call a command dozens of times on a dozen of different pages in a product, due to unwanted code duplication and complicated code maintenance. In case the developers team decides to change the ID Attribute of the element.

Hence, the Page Object Pattern technique provides a solution for working with multiple web pages and prevents unwanted code duplication and enables an uncomplicated solution for code maintenance. In general, every page in our application will be represented by a unique class of its own and the page element inspection will be implemented in every class.

In this tutorial, we’ll be using more than one class (the same one the automated tests were executed from). We’ll begin using layers.

Let’s begin with calling each element from the class and operate the desired method. The following example will demonstrate this process in the best way:

We’ll be implementing the Page Object Pattern on the Contact Us page. First, let’s get back to the test automation project and create the new folder ‘Pages’. In this folder we’ll be creating a new class called ‘ContactUs’.

 

 

 

Copy the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;
using System.Threading;
using NUnit.Framework;
using System.IO;

namespace Test
{
    public class ContactUs
    {
        [FindsBy(How =How.Name,Using = "your-name")]
        private IWebElement yourName;

        [FindsBy(How = How.Name, Using = "your-email")]
        private IWebElement yourEmail;
        [FindsBy(How = How.Name, Using = "your-subject")]
        private IWebElement yourSubject;

        [FindsBy(How = How.Name, Using = "your-message")]
        private IWebElement yourMessage;

        [FindsBy(How = How.XPath, Using = "//input[@type='submit']")]
        private IWebElement submit;

        [FindsBy(How = How.ClassName, Using = "wpcf7-response-output")]
        private IWebElement SuccMessage;

        [FindsBy(How = How.Id, Using = "menu-item-1296")]
        private IWebElement contactUs;
        public bool isAt()
        {
            return Browsers.Title.Contains("Contact Us");
        }

        public void GoTo()
        {
            contactUs.Click();
        }

        public void SendYourName(string name)
        {
            yourName.SendKeys(name);
        }

        public void SendYourEmail(string email)
        {
            yourEmail.SendKeys(email);
        }

        public void SendYourSubject(string Subject)
        {
            yourSubject.SendKeys(Subject);
        }

        public void SendYourMessage(string massage)
        {
            yourMessage.SendKeys(massage);
        }

        public void clickSubmit()
        {
            submit.Click();
        }

        public void ValidateMessage()
        {
            try
            {
                var text = SuccMessage.Text;
            }
            catch (Exception e)
            {
                Assert.Fail();
            }
        }
    }
}

 

FindsBy attribute:

[FindsBy(How = How.Name, Using = “your-name”)]

private IWebElement yourName;

The tag will be on top of the object of the web element type. FindsBy receives 2 parameters for locating the element.

In the following example we’ll be locating the element by ‘Name’ while Using is the value of the Selector. For example:

 

 

In the same way we’ll map the elements on the Contact Us page.
After all the elements are mapped, lets create functions performing actions on these elements:

public void Goto()
{
    contactUs.Click();
}

public void SendYourName(string name)
{
    yourName.SendKeys(name);
}

 

GoTo function – clicks on the element on the navigation bar and navigates to the Contact Us page.

SendYourName function – sends values to the same field.

After creating the class handling our page, we’ll be implementing it by creating another class names ‘Pages’. This class will consist of the entire set of page objects we have. It’s going to be the only class that can communicate with each page.

Continue on by pasting the following code:

using OpenQA.Selenium.Support.PageObjects;

namespace Test
{
    public static class Pages
    {
        private static T getPages<T>() where T : new ()
        {
            var page = new T();
            PageFactory.InitElements(Browsers.getDriver, page);
            return page;
        }

        public static ContactUs contactUs
        {
            get { return getPages<ContactUs>(); }
        }
    }
}
  • private static T getPages<T>() – Creates a new instance to the specified page and returns the page.
  • public static ContactUs contactUs – Returns an instance of ContactUs page.

 

Using the same function (getPages) and generally this is the reason why it’s private, because it’s possible to call it solely from pages.

It’s important to mention that every page we instantiate, we’ll have to add it to the ‘Pages’ class because the page is accessible only via this class.

I’ll demonstrate with an example:

Assuming we will be writing automated tests and will want to click on the Contact Us page, the following command should be written:

Pages.contactUs.Goto();


Continue on to the Next step in the tutorial and Learn how to Read Data From CSV File in C#.

Feel free to leave your questions/ideas in the comment section below!


 

Asya Galinsky

About the author

Asya Galinsky

Join TestProject Community

Get full access to the world's first cloud-based, open source friendly testing community. Enjoy TestProject's end-to-end test automation Platform, Forum, Blog and Docs - All for FREE.

Join Us Now  

Comments

12 13 comments
  • Avatar
    teddd May 16, 2017, 12:41 pm

    great guide !
    how can i implement Cross Browser testing you mentioned in this guide with the page object pattern?

  • Asya Galinsky
    Asya Galinsky May 17, 2017, 3:07 pm

    Hi, glad you found this tutorial helpful! To get extra info on page object pattern and cross browser testing, I recommend reading the following tutorial: https://blog.testproject.io/2017/02/09/cross-browser-testing-selenium-webdriver/

    Good luck 🙂

  • Avatar
    mkozak81 September 7, 2017, 12:35 pm

    Hi Asya,

    Great tutorial.
    I’m using the same approach (using Page.cs class). Where is the best place to prepare a WebDriverWait function e.g. WaitForElement, to be used in all PO classes ? (or it’s not needed at all) ? Thanks for the hint

    • Asya Galinsky
      Asya Galinsky September 7, 2017, 2:20 pm

      Hi,
      I’m glad you liked this tutorial 🙂
      An implicit wait can be for all object when using page objects pattern, so that following will work.
      waiter.until(ExpectedConditions.visibilityOf(page.object));
      Explicit waits should not be a part of page objects class but a part of the flow.
      Best of luck!

      • Avatar
        mkozak81 September 7, 2017, 7:38 pm

        Not sure I understood…what do you mean by saying “”…a prat of the flow” ?
        Suppose I have:
        – Pages class where I initialize page elements (exactly like in your example above)
        – PO class “Home Page”
        – and the TC class
        Should wait.Until be a part of Factory (to allow each PO class use it while executing methods on it’s elements) ?
        Maybe it’s a good idea to add such section into your tutorial ?

        • Oren Nahum
          Oren Nahum September 20, 2017, 4:15 pm

          Hi Mkozak81,
          I think what Asya meant was, assuming your test case flow is something like:
          var element = findElement(By.Id(“someid”);
          element.click();

          And, you fear this element will not be present in the first few seconds, so you would want a Wait – an Excplicit wait, it should be inserted into the code in that flow, before element.click().

          However, if you want to use implicit waits, it can be a nice idea to put them inside the PO methods for calling those elements, like you wrote.

  • Avatar
    rose_123 March 23, 2018, 9:15 pm

    Hi Asya,
    Thanks for this tutorial. It was very helpful! I have a question for you though. Pagefactory is going to be deprecated very soon. Can you please show us how to modify the existing code in your tutorial based on that? I am asking about these specific lines:
    private static T getPages() where T : new ()
    {
    var page = new T();
    PageFactory.InitElements(Browsers.getDriver, page);
    return page;
    }

  • Avatar
    dev.ssrj June 18, 2018, 1:32 pm

    Hi Asya Galinsky ,

    Can anybody share this simple Framework which is working, so that it will be helpful to understand the full flow of it.

    Thanks!!

  • Avatar
    Laksh September 11, 2019, 10:48 pm

    is the code available? I am not sure how in Page class how the WebElements are resolved when Test runs. I am getting null reference.

  • Avatar
    Laksh September 11, 2019, 10:48 pm

    Also where is PageFactory?

Leave a Reply

Join TestProject Newsletter

Join a 20K community of readers! Always stay up-to-date with all the latest test automation trends, best practice and tips shared by leading software testing community experts across the globe!

FacebookLinkedInTwitterEmail