Introduction
In my new series of articles “Design Patterns in Automation Testing“, I am going to present you the most useful techniques for structuring the code of your automation tests. In my examples, I am going to use Selenium WebDriver. However, the patterns are abstract structures, and they can be implemented with any similar automation framework or programming language. One of the most popular patterns in Web Automation is the so called Page Object Pattern. To understand the primary goal of the pattern, first you need to think what your web automation tests are doing. They navigate to different web pages and click/type on/in various elements. The Page Object Pattern wraps all elements, actions and validations happening on a page in one single object- Page Object. I am going to show you two ways in which you can use this pattern. Through the usage of the built-in Page Objects in Selenium WebDriver and via manually created classes.
Page Object Pattern via Selenium WebDriver
UML Class Diagram
Participants
The classes and objects participating in this pattern are:
- Page Object (
BingMainPage
) - Contains properties of all necessary web elements of the page. Also, there you can find all actions that can be performed (Search, Navigate). All validations can be placed here too. - UI Tests (
BingTests
) – This class contains a group of tests related to the above page; it can hold only a single instance of the page object.
Page Object Pattern C# Code
If you are not familiar with the WebDriver Framework, you can look into my article- Getting Started with WebDriver C# in 10 Minutes. To be able to use the page object pattern built-in feature of the framework, you need to install an additional NuGet Package - Selenium.Support
.
The automation test cases in the below examples will navigate to Bing, search for “Automate The Planet” and validate the count of the returned results. In this particular case, there is a need for only two classes - BingMainPage
and the test class BingTests
.
public class BingMainPage
{
private readonly IWebDriver driver;
private readonly string url = @"http://www.bing.com/";
public BingMainPage(IWebDriver browser)
{
this.driver = browser;
PageFactory.InitElements(browser, this);
}
[FindsBy(How = How.Id, Using = "sb_form_q")]
public IWebElement SearchBox { get; set; }
[FindsBy(How = How.Id, Using = "sb_form_go")]
public IWebElement GoButton { get; set; }
[FindsBy(How = How.Id, Using = "b_tween")]
public IWebElement ResultsCountDiv { get; set; }
public void Navigate()
{
this.driver.Navigate().GoToUrl(this.url);
}
public void Search(string textToType)
{
this.SearchBox.Clear();
this.SearchBox.SendKeys(textToType);
this.GoButton.Click();
}
public void ValidateResultsCount(string expectedCount)
{
Assert.IsTrue(this.ResultsCountDiv.Text.Contains(expectedCount),
"The results DIV doesn't contains the specified text.");
}
}
There are two important things to highlight in the above example.
[FindsBy(How = How.Id, Using = "sb_form_q")]
public IWebElement SearchBox { get; set; }
Just add a property with the desired name and place the above attribute with the appropriate properties - the type of the locator (Id
, Class
, CSS
) and the value of the Locator
.
Initialization of the Elements
public BingMainPage(IWebDriver browser)
{
this.driver = browser;
PageFactory.InitElements(browser, this);
}
The static
method InitElements
takes the responsibility to initialize all of the elements on the page the first time when the driver navigates to it.
Usage in Tests
[TestClass]
public class BingTests
{
public IWebDriver Driver { get; set; }
public WebDriverWait Wait { get; set; }
[TestInitialize]
public void SetupTest()
{
this.Driver = new FirefoxDriver();
this.Wait = new WebDriverWait(this.Driver, TimeSpan.FromSeconds(30));
}
[TestCleanup]
public void TeardownTest()
{
this.Driver.Quit();
}
[TestMethod]
public void SearchTextInBing_First()
{
BingMainPage bingMainPage = new BingMainPage(this.Driver);
bingMainPage.Navigate();
bingMainPage.Search("Automate The Planet");
bingMainPage.ValidateResultsCount("264,000 RESULTS");
}
}
The main pros of the Page Object Pattern are the readability and that it follows the DRY (Do not Repeat Yourself) and Single Responsibility SOLID Principles. It helps you to build an easy to maintain test that does not use element locators directly.
Page Object Pattern without Selenium WebDriver
UML Class Diagram
The classes and objects participating in this pattern are:
- Page Object Element Map – Contains all element properties and their location logic.
- Page Object Validator – Consists of the validations that will be performed on the page.
- Page Object (
BingMainPage
)- Holds the actions that can be performed on the page like Search and Navigate. Exposes an easy access to the Page Validator through the Validate()
method. The best implementations of the pattern hide the usage of the Element Map, wrapping it through all action methods. - UI Tests (
BingTests
) – This class contains a group of tests related to the above page; it can hold only a single instance of the page object.
Page Object Pattern C# Code
Using this approach, we don’t need anymore the Selenium.Support
NuGet.
Paste all elements of the page in a new element map class.
public class BingMainPageElementMap
{
private readonly IWebDriver browser;
public BingMainPageElementMap(IWebDriver browser)
{
this.browser = browser;
}
public IWebElement SearchBox
{
get
{
return this.browser.FindElement(By.Id("sb_form_q"));
}
}
public IWebElement GoButton
{
get
{
return this.browser.FindElement(By.Id("sb_form_go"));
}
}
public IWebElement ResultsCountDiv
{
get
{
return this.browser.FindElement(By.Id("b_tween"));
}
}
}
Paste all validation methods in a separate class.
public class BingMainPageValidator
{
private readonly IWebDriver browser;
public BingMainPageValidator(IWebDriver browser)
{
this.browser = browser;
}
protected BingMainPageElementMap Map
{
get
{
return new BingMainPageElementMap(this.browser);
}
}
public void ResultsCount(string expectedCount)
{
Assert.IsTrue(this.Map.ResultsCountDiv.Text.Contains(expectedCount),
"The results DIV doesn't contains the specified text.");
}
}
At last, put together all classes in the Main Page Object.
public class BingMainPage
{
private readonly IWebDriver browser;
private readonly string url = @"http://www.bing.com/";
public BingMainPage(IWebDriver browser)
{
this.browser = browser;
}
protected BingMainPageElementMap Map
{
get
{
return new BingMainPageElementMap(this.browser);
}
}
public BingMainPageValidator Validate()
{
return new BingMainPageValidator(this.browser);
}
public void Navigate()
{
this.browser.Navigate().GoToUrl(this.url);
}
public void Search(string textToType)
{
this.Map.SearchBox.Clear();
this.Map.SearchBox.SendKeys(textToType);
this.Map.GoButton.Click();
}
}
By the way, during my research for the "Design Patterns in Automation Testing" series, I always first read about the presented pattern in several books. One of them that you might want to check is "Head First Design Patterns" by Eric Freeman. The author uses a very unique methodology for presenting the material that I haven't found anywhere else. Probably most of you will like it. For the more hardcore fans that might find the book too easy, I recommend the bible of the design patterns - "Design Patterns- Elements of Reusable Object-Oriented Software". It will change your way of thinking about object-oriented design.
Usage in Tests
[TestClass]
public class BingTests
{
public IWebDriver Driver { get; set; }
public WebDriverWait Wait { get; set; }
[TestInitialize]
public void SetupTest()
{
this.Driver = new FirefoxDriver();
this.Wait = new WebDriverWait(this.Driver, TimeSpan.FromSeconds(30));
}
[TestCleanup]
public void TeardownTest()
{
this.Driver.Quit();
}
[TestMethod]
public void SearchTextInBing_Second()
{
POP.BingMainPage bingMainPage = new POP.BingMainPage(this.Driver);
bingMainPage.Navigate();
bingMainPage.Search("Automate The Planet");
bingMainPage.Validate().ResultsCount("264,000 RESULTS");
}
}
As you can see, the usage of the second type of page object pattern structure is identical to the previous one. Regardless, I like the second solution more because it follows the SOLID Principles more strictly. The different classes used in it have a stronger Cohesion.
So Far in the "Design Patterns in Automated Testing" Series
- Page Object Pattern
- Advanced Page Object Pattern
- Facade Design Pattern
- Singleton Design Pattern
- Fluent Page Object Pattern
- IoC Container and Page Objects
- Strategy Design Pattern
- Advanced Strategy Design Pattern
- Observer Design Pattern
- Observer Design Pattern via Events and Delegates
- Observer Design Pattern via IObservable and IObserver
- Decorator Design Pattern- Mixing Strategies
- Page Objects That Make Code More Maintainable
- Improved Facade Design Pattern in Automation Testing v.2.0
- Rules Design Pattern
- Specification Design Pattern
- Advanced Specification Design Pattern
If you enjoy my publications, feel free to SUBSCRIBE
Also, hit these share buttons. Thank you!
Source Code
The post- Page Object Pattern in Automation Testing appeared first on Automate The Planet.
All images are purchased from DepositPhotos.com and cannot be downloaded and used for free. License Agreement