C# Selenium Tutorial Part 2

In this article we will take a look at how to implement the PageObject model into our solution.  Using PageObjects enables you to group your page element locators and page actions into a single class.  You can quickly build tests using these class functions.  Also, any page specific edits in the future can be made in a centralized location.

In this example we are going to perform a search on Amazon and navigate to the second page of search results.  First create two page classes.  One is for the home page (AmazonHome.cs), and the other is for the search results page (AmazonResults.cs).

AmazonHome.cs

using System;
using System.Linq;
using System.Text;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Support.PageObjects;
using OpenQA.Selenium.Support.UI;

namespace SeleniumDemo
{
 public class AmazonHome
 {
   private readonly string amazonUrl = "https://www.amazon.com/";
   private readonly IWebDriver driver;

   public AmazonHome(IWebDriver driver)
   {
     this.driver = driver;
   }

  //web element locators
  private readonly string searchDropdown = "searchDropdownBox";
  private readonly string searchTextbox = "twotabsearchtextbox";
  private readonly string searchButton = "//*[@id='nav-search']/form/div[2]/div/input";

  //this test action is a collection of class methods
  public AmazonHome AmazonSearch(string searchDept, string searchString)
  {
   try
   {
    AmazonNavigate();
    SelectDepartment(searchDept);
    EnterSearchCriteria(searchString);
    ClickSearch();
   }
   catch (Exception e)
   {
    Console.WriteLine("Encountered an error during item search. Stack trace = " + e.Message);
   }
   return this;
  }

  //navigate to Amazon URL
  public AmazonHome AmazonNavigate()
  {
   try
   {
    driver.Navigate().GoToUrl(amazonUrl);
   }
   catch (Exception e)
   {
    Console.WriteLine("Encountered a navigation error. Stack trace = " + e.Message);
   }
   return this;
  }

  //select by text in dropdown
  public AmazonHome SelectDepartment(string selectText)
  {
   try
   {
    //create a SelectElement and locate dropdown item by text
    SelectElement deptSelect = new SelectElement(driver.FindElement(By.Id((searchDropdown))));
    deptSelect.SelectByText(selectText);
   }
   catch (Exception e)
   {
    Console.WriteLine("Encountered an error when trying to access dropdown. Stack trace = " + e.Message);
   }
   return this;
  }

  //enters text into search textbox
  public AmazonHome EnterSearchCriteria(string searchText)
  {
   driver.FindElement(By.Id(searchTextbox)).SendKeys(searchText);
   return this;
  }

  //clicks search button
  public AmazonHome ClickSearch()
  {
   driver.FindElement(By.XPath(searchButton)).Click();
   return this;
  }
 }
}

AmazonResults.cs

using System;
using System.Linq;
using System.Text;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Support.PageObjects;
using OpenQA.Selenium.Support.UI;

namespace SeleniumDemo
{
 public class AmazonResults
 {
  private readonly IWebDriver driver;

  public AmazonResults(IWebDriver driver)
  {
   this.driver = driver;
  }

  //web element locators
  private readonly string resultsNext = "//*[@id='pagnNextLink']/span[2]";

  //click method
  public AmazonResults AmazonClickNext()
  {
   try
   {
    driver.FindElement(By.XPath(resultsNext)).Click();
   }
   catch (Exception e)
   {
    Console.WriteLine("Encountered an error while trying to click next button. Stack trace = " + e.Message);
   }
   return this;
  }
 }
}

In the Program.cs we will create instances of these PageObject classes and execute the search workflow.

using System;
using System.Linq;
using System.Text;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;

namespace SeleniumDemo
{
 class Program
 {
  static void Main(string[] args)
  {
  //test vars
  string itemDept = "CDs & Vinyl";
  string itemName = "Van Halen";

  //setup chromedriver
  var options = new ChromeOptions();
  options.AddArguments("test-type");
  options.AddArgument("--start-maximized");
  IWebDriver driver = new ChromeDriver(@"C:\chromedriver", options);

  try
  {
   //instance of AmazonHome PageObject
   AmazonHome amazonHomePageObject = new AmazonHome(driver);
   amazonHomePageObject.AmazonSearch(itemDept, itemName);
   //instance of AmazonResults PageObject
   AmazonResults amazonResultsPageObject = new AmazonResults(driver);
   amazonResultsPageObject.AmazonClickNext();
  }
  catch (Exception e)
  {
   Console.WriteLine("Encountered an unforeseen error. Stack trace = " + e.Message);
  }

  //close chromedriver
  driver.Quit();
  }
 }
}

First you should see a browser launch and do a search on Amazon.

01_vhdlr

Then you should see the Next Page button get clicked and proceed to the second page of search results.

01_vhdiver

At the end of the test, the browser should close itself.

Note that some of web element types were handled differently:

SelectElement deptSelect = new SelectElement(driver.FindElement(By.Id((searchDropdown))));
deptSelect.SelectByText(selectText);

We handled the dropdown by creating a SelectElement object.  Then we selected a specific item in the dropdown using the text property of the dropdown.

Also, we introduced the concept of a test action.  A call to the AmazonSearch function performs four consecutive actions:

    // 1) Navigates to the Amazon home page
    AmazonNavigate();
    // 2) Selects the search category in the dropdown
    SelectDepartment(searchDept);
    // 3) Enters the search text
    EnterSearchCriteria(searchString);
    // 4) Clicks the search button
    ClickSearch();

I would recommend ordering these actions in the same order they occur on the webpage.  This way your code will make more sense to someone else reading your code and it will be easier to determine where updates should be made in the future.

Think about how you would prefer to group together page specific test actions.  After grouping, think about how you you like to drive a chain of events by calling consecutive test actions.  Maybe you want to group all of your test actions into a keyword.  These are the types of questions you should be asking while developing your test framework.