Enroll in Selenium Training

Factory Design Pattern

Factory Design Pattern is one of the most useful patterns. You can see the usage of this pattern everywhere. One such usage of Factory Design Principle in Frameworks is in the PageFactory. As we all know PageFactory is the brilliant page object pattern provided by Selenium. A typical usage of Selenium PageFactory is something like this.

PageFactory.InitElements(driver, PageObjectClass);

Here you will notice that we are not directly creating a  PageObject by using the new keyword. We are calling another class called PageFactory and on that class, we have a static method called InitElements(). This InitElements() method is responsible for initializing all the page objects. PageFactory is based on the Factory pattern design principle, it is basically a factory that stores and provides different page objects for usage.

A Factory pattern is used to encapsulate the complexity of creating an object. Below are the two important ideas behind Factory pattern:

  • Hide the logic of initializing an object inside the factory.
  • Refer to the object using a common interface instead of the concrete class.
  • Maintain/Cache the newly created object’s life time. This is not main responsibility of a factory pattern.

Let's understand how a Factory Pattern is beneficial while designing a Selenium Automation Framework.

Browser Factory or WebDriver Factory

From our experience with frameworks we know that maintaining and passing around a WebDriver object across different tests is a delicate process. Also, the complexity increases when we have to maintain only one instance of a WebDriver through out the test run. To overcome the problem on instantiation of WebDriver and maintaining the instance of browser we can use create a small class called Browser Factory or WebDriver Factory

IWebDriver driver = BrowserFactory.InitBrowser("Firefox");

or it can be like below, your choice of names :)

IWebDriver driver = WebDriverFactory.InitDriver("Firefox");

There has to be a InitBrowser() or InitDriver() method inside the BrowserFactory or WebDriverFactory class, which will initialize the requested browser for the test. The above code do the same. By using the InitBrowser() method of the BrowserFactory class, it is requesting the class to return the Firefox browser to the test.

Now, how would you handle this in the Browser/Driver factory. It is completely up to you. You can use IF ELSE condition on the browser parameter and return browser. But I would like to make the best use of the Advance C# feature, so I would use Dictionary object to do the same.

Dictionary<TKey, TValue>is a generic collection included in the System.Collection.Generics namespace. TKey denotes the type of key and TValue is the type of TValue. A Dictionary can be initialized with a variable of IDictionary<Tkey, TValue> interface as well as with a Dictionary<TKey, Tvalue> class. It is recommended to program to the interface rather than to the class. So, use IDictionary<TKey, TValue> type variable to initialize a dictionary object.

In the below example, we have specified types of key and value while declaring a dictionary object. A string is a type of key and IWebDriver is a type of value that will be stored into a dictionary object named Drivers. You can use any valid C# data type for keys and values.

private static readonly Dictionary<string, IWebDriver> Drivers = new Dictionary<string, IWebDriver>();

Note: Dictionary cannot include duplicate or null keys, whereas values can be duplicated or set as null. Keys must be unique otherwise it will throw a runtime exception.

Dictionary Object Methods

  • Count : Gets the total number of elements exists in the Dictionary<TKey,TValue>
  • IsReadOnly : Returns a boolean indicating whether the Dictionary<TKey,TValue> is read-only
  • Item : Gets or sets the element with the specified key in the Dictionary<TKey,TValue>
  • Keys: Returns collection of keys of Dictionary<TKey,TValue>
  • Values : Returns collection of values in Dictionary<TKey,TValue>

Browser Factory or WebDriver Factory Implementation in Selenium C#

Create Browser Factory

  1. Create a new folder and name it as WrapperFactory.

  2. Create a new C# class in the WrapperFactory folder, name it as BrowserFactory or WebDriverFactory

  3. Write the implementation of the BrowserFactory class.

BrowserFactory Class

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.IE;
using System;
using System.Collections.Generic;

namespace OnlineStore.WrapperFactory
{
    class BrowserFactory
    {
        private static readonly IDictionary<string, IWebDriver> Drivers = new Dictionary<string, IWebDriver>();
        private static IWebDriver driver;

        public static IWebDriver Driver
        {
            get
            {
                if(driver == null)
                    throw new NullReferenceException("The WebDriver browser instance was not initialized. You should first call the method InitBrowser.");
                return driver;
            }
            private set
            {
                driver = value;
            }
        }

        public static void InitBrowser(string browserName)
        {          
            switch (browserName)
            {
                case "Firefox":
                    if (Driver == null)
                    {
                        driver = new FirefoxDriver();
                        Drivers.Add("Firefox", Driver);
                    }
                    break;

                case "IE":
                    if (Driver == null)
                    {
                        driver = new InternetExplorerDriver(@"C:\PathTo\IEDriverServer");
                        Drivers.Add("IE", Driver);
                    }
                    break;

                case "Chrome":
                    if (Driver == null)
                    {
                        driver = new ChromeDriver(@"C:\PathTo\CHDriverServer");
                        Drivers.Add("Chrome", Driver);
                    }
                    break;
            }            
        }

        public static void LoadApplication(string url)
        {
            Driver.Url = url;
        }

        public static void CloseAllDrivers()
        {
            foreach (var key in Drivers.Keys)
            {
                Drivers[key].Close();
                Drivers[key].Quit();
            }
        }
    }
}

Key points in the above implementation:

  1. A Dictionary Object of Browser name to Browser is stored as a private member. So nobody can directly access it.
  2. In the InitBrowser() method a browser is created only once, that will be when the very first call to get the browser of a particular type will come. After that only the existing browser is returned. This means that the browser will be cached through the session.
  3. Complexity of browser initialization and storage is abstracted out from tests. This means that tests will no longer have to worry about where the browser came from or where it is stored. All they have to do is call the BrowserFactory.InitBrowser(<BrowserName>) method.
  4. This will enable tests to get any type of drivers without worrying of creating unnecessary driver instances.
  5. Any of the Class now can get the instance of WebDriver by using BrowserFactory.Driver.

Cons of this approach

  • As you can see that this is not thread safe, it means that it will not be able to work on parallel test environments. A few modifications in this class and it will become capable of handling parallel requests. We will learn about it in coming articles on design principles in test frameworks.

The Main Idea behind the BrowserFactory in the Framework

  • We should not be using driver object directly in the test. Means there should not be any statement where we use driver and perform any action on it. At present we have one statement in the test to open the Url using driver object. Now instead of that we will use LoadApplication method to open the Url.

LogInTest TestCase

using NUnit.Framework;
using OnlineStore.PageObjects;
using OnlineStore.WrapperFactory;
using OpenQA.Selenium;
using System.Configuration;

namespace OnlineStore.TestCases
{
    class LogInTest
    {
        [Test]
        public void Test() {

            BrowserFactory.InitBrowser("Firefox");
            BrowserFactory.LoadApplication(ConfigurationManager.AppSettings["URL"]);    

            var homePage = new HomePage(driver);
            homePage.ClickOnMyAccount();

            var loginPage = new LoginPage(driver);
            loginPage.LoginToApplication("LogInTest");

            BrowserFactory.CloseAllDrivers();
        }    
    }
}

Project Solution Explorer

Browser Factory

To get the complete code for all the projects, please visit the previous chapter of Data Driven Testing.

Data Driven Testing
Data Driven Testing
Previous Article
Page Generator
Page Generator
Next Article
Lakshay Sharma
I’M LAKSHAY SHARMA AND I’M FULL STACK TEST AUTOMATION ENGINEER. Have passed 12 years playing with automation in mammoth projects like O2 (UK), Sprint (US), TD Bank (CA), Canadian Tire (CA), NHS (UK) & ASOS(UK). Currently I am working with KNAB bank as SDET. I am passionate about designing Automation Frameworks that follow OOPS concepts and Design patterns. https://www.linkedin.com/in/lakshay-sharma-a4216312/
Reviewers
Virender Singh's Photo
Virender Singh

Similar Articles