Archive

Archive for May, 2014

Test Automation Framework in Page-Object pattern using C#, Selenium, SpecFlow and NUnit

May 20, 2014 6 comments

Creating a Test Automation Framework is a challenging task due to following reasons:

  • Test framework design and the coding of that design requires significant time and effort
  • Test framework must be easy to expand and maintain
  • Test framework should be independent of application
  • Test design should remove most testers from the complexities of the test framework

Let me explain the creation of framework using the Page-Object pattern. Refer my previous blog for more info on this pattern.

SOFTWARE REQUIREMENTS:

  • Windows 7 as OS
  • Microsoft Visual Studio 2010 as IDE
  • SpecFlow as BDD tool for .Net
  • NUnit as Unit Testing Tool
  • Selenium as a Test Automation Tool

SET-UP INSTRUCTIONS:

Refer my previous blog on set-up instructions for creation and execution of automation tests using C#, Selenium, SpecFlow and NUnit.

Lets consider a test scenario:

Given I am on the Google home page
When I search for text selenium
Then I should see the search results

My task is to create a Page-Object pattern for the above test scenario. Contents in the files such as  GoogleSearch.feature and WebBrowser.cs are unchanged and I had defined the test actions using Selenium API within the GoogleSearchStepDefinition.cs file in my previous blog. Now, I need to apply the Page-Object pattern by creating a class for each web page. Create 2 class files as the test scenario has actions w.r.t 2 pages namely – GoogleHomePage.cs and GoogleSearchResultsPage.cs and this class file will be placed within a folder ‘Pages’ to separate from other class files. Now, I create methods within the GoogleSearchStepDefinition.cs file and these methods are executed within the page class GoogleHomePage.cs and GoogleSearchResultsPage.cs.

Copy and paste the code below to the  GoogleSearchStepDefinition.cs file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using System.Threading;
namespace SpecflowTest
{
 [Binding]
 public class StepDefinition1
 {
 [Given(@"I am on the Google home page")]
 public void GivenIAmOnTheGoogleHomePage()
  {
  var GoogleHP = new GoogleHomePage(WebBrowser.Current);
  GoogleHP.Navigate();
  }

 [When(@"I search for text (.*)")]
 public void WhenISearchForATextSelenium(string keyword)
  {
  var GoogleHP = new GoogleHomePage(WebBrowser.Current);
  GoogleHP.SearchOperation(keyword);
  }

 [Then(@"I should see the search results")]
 public void ThenIShouldSeeTheSearchResultsForSelenium()
  {
  var GoogleSRP = new GoogleSearchResultsPage(WebBrowser.Current);
  GoogleSRP.ValidateTest();
  }
 }
}

Copy and paste the code below to the  GoogleHomePage.cs file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using System.Threading;
using SpecflowTest;
namespace SpecflowTest
{
 class GoogleHomePage
 {
 private IWebDriver driver;

 //Contructor to re-use the driver
 public GoogleHomePage(IWebDriver driver)
 {
 this.driver = WebBrowser.Current;
 }

 public void Navigate()
 {
 //Navigate to the site
 WebBrowser.Current.Navigate().GoToUrl("http://www.google.com");
 //Check that the Title is what we are expecting
 Assert.AreEqual("Google", WebBrowser.Current.Title);
 }

 public void SearchOperation(string keyword)
 {
 // Find the text input element by its name
 IWebElement query = WebBrowser.Current.FindElement(By.Name("q"));
 // Input the search text
 query.SendKeys(keyword);
 // Now submit the form
 query.Submit();
 // Google's search is rendered dynamically with JavaScript.
 // Wait for the page to load, timeout after 5 seconds
 WebDriverWait wait = new WebDriverWait(WebBrowser.Current, TimeSpan.FromSeconds(5));
 IWebElement title = wait.Until<IWebElement>((d) =>
 {
  return d.FindElement(By.ClassName("ab_button"));
 });
 }
 }
}

Copy and paste the code below to the  GoogleSearchResultsPage.cs file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using System.Threading;
using SpecflowTest;
namespace SpecflowTest
{
 class GoogleSearchResultsPage
 {
 private IWebDriver driver;

 //Contructor to re-use the driver
 public GoogleSearchResultsPage(IWebDriver driver)
 {
 this.driver = WebBrowser.Current;
 }

 public void ValidateTest()
 {
 //Check that the Title is what we are expecting
 Assert.AreEqual("selenium - Google Search", WebBrowser.Current.Title);
 }
 }
}

Run the tests using NUnit and the tests will Pass. Now, we are almost 90% done with our task in the creation of a test framework in Page-Object pattern but we need to re-factor this pattern by creating a Base Page class. The need for the base class is to remove the redundant methods. In the GoogleHomePage.cs and GoogleSearchResultsPage.cs file, there is a redundant method on initializing the driver:

private IWebDriver driver;

 //Contructor to re-use the driver
 public GoogleSearchResultsPage(IWebDriver driver)
 {
 this.driver = WebBrowser.Current;
 }

So, create a Base Page class say BasePage.cs and move the above method on initialising the driver to the Base Page Class as shown below:

private IWebDriver driver;

 protected BasePage(IWebDriver driver, String loadUrl = "")
 {
 this.driver = WebBrowser.Current;
 WebUrl = loadUrl;
 }

Similarly, I have added a method on navigating to the webpage to the BasePage.cs as shown below:

private readonly string WebUrl;

public void GoToSite()
 {
 driver.Navigate().GoToUrl(WebUrl);
 }
 
 
Final contents of GoogleHomePage.cs as shown below:
             
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using System.Threading;
using SpecflowTest;
namespace SpecflowTest
{
 class GoogleHomePage : BasePage
 {
 private static readonly String Url = "http://www.google.com";
//Contructor to re-use the driver
 public GoogleHomePage(IWebDriver driver) : base(driver, Url)
 {
 }

 public void Visit(string Url)
 {
 GoToSite();
 //Check that the Title is what we are expecting
 Assert.AreEqual("Google", WebBrowser.Current.Title);
 }

 public void SearchOperation(string keyword)
 {
 // Find the text input element by its name
 IWebElement query = WebBrowser.Current.FindElement(By.Name("q"));
 // Input the search text
 query.SendKeys(keyword);
 // Now submit the form
 query.Submit();

 // Google's search is rendered dynamically with JavaScript.
 // Wait for the page to load, timeout after 5 seconds
 WebDriverWait wait = new WebDriverWait(WebBrowser.Current, TimeSpan.FromSeconds(5));
 IWebElement title = wait.Until<IWebElement>((d) =>
 {
 return d.FindElement(By.ClassName("ab_button"));
 });
 }
 }
}
      
    
Final contents of GoogleSearchResultsPage.cs as shown below:
      

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using System.Threading;
using SpecflowTest;

namespace SpecflowTest
{
 class GoogleSearchResultsPage : BasePage
 {
 //Contructor to re-use the driver
 public GoogleSearchResultsPage(IWebDriver driver) : base(driver)
 {
 }

 public void ValidateTest()
 {
 //Check that the Title is what we are expecting
 Assert.AreEqual("selenium - Google Search", WebBrowser.Current.Title);
 }
 }
}

 
 
Final contents of BasePage.cs as shown below:
     
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using System.Threading;
using SpecflowTest;
namespace SpecflowTest
{
 class BasePage
 {
 private IWebDriver driver;
 private readonly string WebUrl;

 protected BasePage(IWebDriver driver, String loadUrl = "")
 {
 this.driver = WebBrowser.Current;
 WebUrl = loadUrl;
 }

 public void GoToSite()
 {
 driver.Navigate().GoToUrl(WebUrl);
 }
}
}
 
 
Run the test using NUnit:

 

Page-Object pattern for creating a Test Automation Framework

May 12, 2014 7 comments

Page-Object pattern is one of the most effective approaches for creating a Test Automation Framework. This pattern maps a GUI page to a page class and the functionality to interact about the page is captured within the Page class (i.e., a new page class created for each new web page). So, you are calling the page class methods that perform action on the web page in your tests.

Test maintenance becomes easier by creating a Base Page class as it contains most common functions that need to be performed on any web page. For example: If need to navigate to a webpage in many of my tests, I may define this in my Base Page Class and then my Page Class must inherit this.

Advantages of using Page-Object pattern in Selenium:
  • Test code is easily readable.
  • Distinguished reusable code.
  • No duplication of Selenium API calls.
  • Creation of tests are easy and improves maintainability.
  • Used effectively with IDE such as Visual Studio, Eclipse, IntelliJ, Visual Studio.
  • Excellent support for languages such as C#, Java, Ruby, Python, Perl.
%d bloggers like this: