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#
- Setting Up the Development Environment for Your Selenium Automation Framework
- How to Inspect Web Elements & Methods to Locate Them with Chrome Devtools
- How to Find Web Elements with Selenium WebDriver
Class 2 – How to Create a Test Automation Framework Architecture with Selenium WebDriver 3
- How to Create a Cross Browser Compatible Testing Framework
- Steps to Develop a Report Module in a Testing Framework
- Implementing a Report Module in an Automated Framework
- How to Write a Functional Test with a Basic Selenium Automation Framework
Class 3 – Utilizing Test Automation Framework with Advanced Capabilities
- You’re here→Page Object Pattern: Advantages & Implementation
- Read Data From CSV File in C#
- How to Create a Test Suite in Selenium WebDriver and C#
- How to Use Data Driven with Selenium Test Suite
Page Object Pattern (POM)
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!
great guide !
how can i implement Cross Browser testing you mentioned in this guide with the page object pattern?
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 🙂
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
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!
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 ?
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.
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;
}
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!!
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.
Hi,you are missing this part:
PageFactory.InitElements(Browsers.getDriver, page);
If the page is not initialized, elements will be nulls.
Also where is PageFactory?
Please have a look at the last code snippet.
Its not available in .NET anymore in Selenium.Support assembly. I am using version 3.141