Page Object Model with C#

In the previous tutorial, we’ve taken the initial steps and entered the world of automated testing. We also wrote our first automated test. From this point on, since we are not just going to write one test, we need to build a framework to test our entire web page or application. For building a framework, we might consider a design pattern for our tests. And one of the most popular test design patterns for selenium tests is Page Object Model. It is also strongly encouraged to use by Selenium’s Wiki.

Page Object Model

First, let’s briefly understand what Page Object Model (POM ) is.

POM is the most widely used design pattern by the Selenium community on which each web page (or significant ones) considered as a different class. On each of this page classes (i.e page objects), you may define the elements of that page and specific methods for that page. Each page object represents the page of the web page or application. It is a layer between the test scripts and UI and encapsulates the features of the page.

Advantages of Page Object Model 

The advantages of page object model can be listed as:

  • Easy to maintain. Once you made your design even any locator or functionality changes on the web page you know exactly where to change your code.
  • Clean and understandable code. Instead of using the same code all over your test framework, many features are defined within the page objects. Hence the lines of codes written is reduced and your code becomes easier to understand.
  • Helps you point out the UI. Since you create page classes for almost every page on your web page, it is easy to comprehend the functionality and model of your web page even at a glance on your test frameworks.
  • Better test scripts. Your test scripts will consist descriptive method names (e.g homepage.goToLoginPage()) instead of incomprehensible and hard-coded code (e.g driver.findElement(By.id(“login”)).click()).

Page Factory

Page Factory is an extension to page objects that are used to initialize the web elements that are defined on the page object. You can define the web elements by using annotation with the help of page factory.

Important Note: From 3.11 version of Selenium C# Bindings, Page Factory is deprecated! 
https://raw.githubusercontent.com/SeleniumHQ/selenium/master/dotnet/CHANGELOG

If you would like to learn more about POM, I suggest you might read Martin Fowler’s article about it.

How to Implement Page Object Model in C#

Let’s start by creating a new project in Visual Studio. I am using Visual Studio 2017 Community Edition which can download here.

And name our project.

Before writing our test cases, let’s create our page objects. In this tutorial, I am going to model www.swtestacademy.com. For the sake of a decent design, let’s create a folder and call it “PageObjects”. We will add page object classes in this folder.

To do that, right-click on the project name (‘POMExample’ in this tutorial), hover on add and click on “New Folder”.

Name it as “PageObjects”.

Now we can add the page object classes inside the folder. In this tutorial, we will cover the home,  about and search result pages. So lets add three classes and name them as “Homepage”, “AboutPage”, “ResultPage” respectively. We are going to model the home page, about and the page that searches are enlisted.

After you add the classes you will have:

It is time to fill inside our page objects. Let’s start with ‘HomePage’ first.

Click on ‘HomePage.cs’ and your homepage class should look exactly like below:

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

namespace POMExample.PageObjects
{
    class HomePage
    {

        private IWebDriver driver;

        public HomePage(IWebDriver driver)
        {
            this.driver = driver;
            PageFactory.InitElements(driver, this);
        }

        [FindsBy(How = How.CssSelector, Using = ".fusion-main-menu a[href*='about']")]
        private IWebElement about;

        [FindsBy(How = How.ClassName, Using = "fusion-main-menu-icon")]
        private IWebElement searchIcon;

        public void goToPage()
        {
            driver.Navigate().GoToUrl("http://www.swtestacademy.com");
        }

        public AboutPage goToAboutPage()
        {
            about.Click();
            return new AboutPage(driver);
        }        
        
    }
}

Let’s understand step by step what our ‘Homepage.cs’ does.

This is our constructor. Each page should use the driver on the test class, therefore, this classes driver equals to the driver passed from the test class. PageFactory.InitElements() is used to [FindsBy] annotation to work.

Here, with the help of Page Factory [FindsBy] annotation finds the web elements on the homepage.

And finally, we define the functionalities of our homepage. Please note that a page object method may return another page object as shown above.

Please copy the ‘AboutPage.cs’ and ‘ResultPage.cs’ respectively.

using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;
using OpenQA.Selenium.Support.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace POMExample.PageObjects
{
    class AboutPage
    {
        private IWebDriver driver;
        private WebDriverWait wait;

        public AboutPage(IWebDriver driver)
        {
            this.driver = driver;
            wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
            PageFactory.InitElements(driver, this);
        }

        [FindsBy(How = How.CssSelector, Using = "#sidebar input[class='s']")]
        private IWebElement searchText;

        public ResultPage search(string text)
        {
            searchText.SendKeys(text);
            wait.Until(ExpectedConditions.ElementToBeClickable(By.CssSelector("#sidebar .searchsubmit"))).Click();
            return new ResultPage(driver);
        }
    }
}
using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace POMExample.PageObjects
{
    class ResultPage
    {
        private IWebDriver driver;

        public ResultPage(IWebDriver driver)
        {
            this.driver = driver;
            PageFactory.InitElements(driver, this);            
        }

        [FindsBy(How = How.CssSelector, Using = "#posts-container>article:nth-child(1)")]
        private IWebElement firstArticle;
        
        public void clickOnFirstArticle()
        {
            firstArticle.Click();
        }
    }
}

We have created our page objects. Now it is time to write our test case(s) by using the page objects.

“Class1.cs” will be our test class so let’s rename it as “TestClass.cs” or you can name it as you wish.

And click “Yes” when asked to change the class name too.

using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using POMExample.PageObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace POMExample
{
    public class TestClass
    {
        private IWebDriver driver;

        [SetUp]
        public void SetUp()
        {
            driver = new ChromeDriver();
            driver.Manage().Window.Maximize();
        }
   
        [Test]
        public void SearchTextFromAboutPage()
        {
            HomePage home = new HomePage(driver);
            home.goToPage();
            AboutPage about = home.goToAboutPage();
            ResultPage result = about.search("selenium c#");
            result.clickOnFirstArticle();
        }

        [TearDown]
        public void TearDown()
        {
            driver.Close();
        }
    }
}

Let’s take a closer look at our test method to understand how we use page object on the test case.

First, we create an instance of the page object so we can call the method from there. Since goToAboutPage() returns AboutPage, we may create the instance of that page object as shown above.

Let’s imagine how we would write this test case without using POM.

How the quality of code changed can be observed easily. If we use these elements on different tests, we have to find them over and over again in each test which will be a huge burden. So implementing POM within your automation frameworks will provide you many advantages.

GitHub Page of the Project
https://github.com/swtestacademy/POMExampleCSharp

Selenium Webdriver Tutorial Series

[fusion_widget_area name=”avada-custom-sidebar-seleniumwidget” title_size=”” title_color=”” background_color=”” padding_top=”” padding_right=”” padding_bottom=”” padding_left=”” hide_on_mobile=”small-visibility,medium-visibility,large-visibility” class=”” id=””][/fusion_widget_area]

Thanks.
Ege

19 thoughts on “Page Object Model with C#”

  1. Hi Ege,

    Greetings.

    Wanted to check if you can train me on C# and development of selenium framework.

    We can have 1×1 discussion on this. Please can you update the course structure along with fee details.

    Thanks,
    Sandeep V

    Reply
  2. Why do I get an error message that a static main function is required as an entry point on executing the above project??
    Need Help regarding this urgently

    Reply
    • Hi gar,
      I will update the example on github on weekend. But assertions belong to test, so they need to be in test. In this example, we clicked on the first example after searching so we can get the title of the page and assert it as a last step of the test.

      Reply
  3. Am getting error in PageFactory tried same code.Not working. Am new to C#

    PageFactory.InitElements(driver, this);

    Reply
    • Please give more detail? We can not and we don’t help anybody with this kind of lack of information. If u open a bug with this kind of output, developers will also create problems. I suggest you provide more details (description, logs, etc.) along with your problems.

      Reply
  4. Tried to run the downloaded code in visual studio 2019 and got the following error
    ========== Starting test run ==========
    Failed to load extensions from file ‘C:\……\POMExampleCSharp-master\packages\NUnit3TestAdapter.3.7.0\tools\NUnit3.TestAdapter.dll’. Please use /diag for more information.
    No test matches the given testcase filter `FullyQualifiedName=POMExample.TestClass.SearchTextFromAboutPage` in C:\…… \POMExampleCSharp-master\POMExample\bin\Debug\POMExample.dll
    ========== Test run finished: 0 Tests (0 Passed, 0 Failed, 0 Skipped) run in 106.7 ms ==========
    Not sure what went wrong I reckon it could be version of NUnit and NUnit Test adapter etc. Please help.

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.