Framework – Your First Framework Part 1

Creating our framework

In the last article, we discussed the definition of a framework and expanded on it. And we tried to understand that the idea of writing your own framework isn’t quite as scary as you might think. 

So today, we’re going to take the first step in to creating your first framework for testing web applications in Selenium. Before we begin, I recommend reading the ‘Selenium – What is it?’ article, so you at least understand the basics of Selenium and WebDriver code. 

The most important part of any Selenium based framework is that it can launch a browser, and have the flexibility to launch a variety of browsers to cover cross browser testing. And that is what our first class is going to be. Rather than do what most sites do when they go through framework tutorials, I’m going to do an article per class, but make sure that by the end of the article, you understand what’s being done, why it’s being done and my thinking behind the way I’ve done. It’s all too easy to assume that you understand code when giving examples, but if I go and put 5 classes in an article and skim across all of them, and you don’t understand any of it, how will the next article be of any help? With that said, let’s begin:

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using System;
using System.Net;
using NUnit.Framework;

namespace AutomationFramework.Engine
{
    public static partial class Driver
    {
        public static TimeSpan TimeOut = new TimeSpan(0, 1, 0);
        public static TimeSpan PollingInterval = new TimeSpan(0, 0, 2);

        public static void SetImplicitTimeout(TimeSpan timeout)
        {
            DriverBase.Instance.Manage().Timeouts().ImplicitWait = timeout;
        }
        
        public static void SetImplicitTimeout()
        {
            SetImplicitTimeout(TimeOut);
        }

        public enum DriverType
        {
            InternetExplorer,
            Chrome,
            Edge
        }

        public static void Initialise(DriverType driverType, TimeSpan? implicitWait)
        {
            var initialiseTimeout = implicitWait ?? TimeOut;

            try
            {
                switch (driverType)
                {
                    case DriverType.InternetExplorer:
                        var internetExplorerOptions = new InternetExplorerOptions { IntroduceInstabilityByIgnoringProtectedModeSettings = true, AcceptInsecureCertificates = true };
                        DriverBase.Instance = new InternetExplorerDriver(TestContext.CurrentContext.TestDirectory, internetExplorerOptions));
                        break;
                    case DriverType.Edge:
                        var edgeExplorerOptions = new EdgeOptions { AcceptInsecureCertificates = true };
                        DriverBase.Instance = new EdgeDriver(TestContext.CurrentContext.TestDirectory, edgeExplorerOptions));
                        break;
                    case DriverType.Chrome:
                        var chromeOptions = new ChromeOptions();
                        chromeOptions.AddArguments("test-type");
                        chromeOptions.AddArguments("chrome.switches", "--disable.extensions");
                        DriverBase.Instance = new ChromeDriver(TestContext.CurrentContext.TestDirectory, chromeOptions, implicitWait);
                        break;
                }
            }
            catch (WebDriverException)
            {
                throw new WebDriverException($"Failed to initialise the {driverType.DescriptionAttribute()} web driver for Selenium");
            }

            DriverBase.Instance.Manage().Window.Maximize();
            
            if(null != implicitWait)
            {
                SetImplicitTimeout((TimeSpan)implicitWait);
            }
        }
    }
}

There are many ways to approach this, but the one we’re going to take in this example is to create our WebDriver instance via a switch statement that will take a parameter at another point in the framework. Depending on the parameter passed, we will create a browser specific WebDriver instance with some options. But let’s break each bit of code down in to detail.

The first thing I should cover is the fact that our class is a partial class. The partial keyword is used to essentially break large classes down in to smaller classes across several files, purely for readability and keeping tidy code. But this is part of a bigger class that is Driver. We’ll cover the other partial classes later on.

Once we get inside the class we get to these few lines:

     public static TimeSpan TimeOut = new TimeSpan(0, 1, 0);
     public static TimeSpan PollingInterval = new TimeSpan(0, 0, 2);

     public static void SetImplicitTimeout(TimeSpan timeout) 
     { 
         DriverBase.Instance.Manage().Timeouts().ImplicitWait = timeout; 
     }

     public static void SetImplicitTimeout() 
     { 
         SetImplicitTimeout(TimeOut); 
     }

These are variables that we will use to set our timeout value before our tests will give up waiting for an action to occur, and how often our framework will check for updates on an event that is possibly about to time out. There are two types of waits to keep in mind with test automation, explicit and implicit. Implicit is basically telling the test to wait for a set period of time, think of a Thread.Sleep. And an explicit wait is telling the test to wait depending on certain conditions. Explicit conditions are always the preferred waits to use when automated testing, purely because, the build up of implicit waits can unnecessarily add time to your test run. It is also considered lazy to overuse implicit waits

After this we create an enum for our different Driver types. Now this is purely a preference thing for me. You could just pass a string in to our initialise method, but it’s far tidier, and less error prone to just pass an enum type in.

On to the Initialise method:

        public static void Initialise(DriverType driverType, TimeSpan? implicitWait)
        {
            var initialiseTimeout = implicitWait ?? TimeOut;

            try
            {
                switch (driverType)
                {
                    case DriverType.InternetExplorer:
                        var internetExplorerOptions = new InternetExplorerOptions { IntroduceInstabilityByIgnoringProtectedModeSettings = true, AcceptInsecureCertificates = true };
                        DriverBase.Instance = new InternetExplorerDriver(TestContext.CurrentContext.TestDirectory, internetExplorerOptions));
                        break;
                    case DriverType.Chrome:
                        var edgeExplorerOptions = new EdgeOptions { AcceptInsecureCertificates = true };
                        DriverBase.Instance = new EdgeDriver(TestContext.CurrentContext.TestDirectory, edgeExplorerOptions));
                        break;
                    case DriverType.Chrome:
                        var chromeOptions = new ChromeOptions();
                        chromeOptions.AddArguments("test-type");
                        chromeOptions.AddArguments("chrome.switches", "--disable.extensions");
                        DriverBase.Instance = new ChromeDriver(TestContext.CurrentContext.TestDirectory, chromeOptions, implicitWait);
                        break;
                }
            }
            catch (WebDriverException)
            {
                throw new WebDriverException($"Failed to initialise the {driverType.DescriptionAttribute()} web driver for Selenium");
            }

            DriverBase.Instance.Manage().Window.Maximize();
            
            if(null != implicitWait)
            {
                SetImplicitTimeout((TimeSpan)implicitWait);
            }
        }

This is where we are setting up which Driver version we will create based on the driverType that we pass in. It’s a simple switch statement that will go in and set up a new instance of the driver and configure any browser specific options that we may want.

These browser settings are completely optional. And you aren’t just limited to the ones that I’m using either. There are many that can be used, and the ones you need, if any, will totally depend on the environment you’re testing on as well as any requirements set in your testing. You can read more about browser specific capabilities and what they mean here.

Once we have set our browser up and done some basic exception checking to make sure it has been properly initialised, we do a basic call to the driver instance to maximise the window. I do it here as opposed to later on, just to make sure that before we’ve even left the initialised method, we have our browser window open and maximised.

It’s worth noting that at the moment we are simply creating a local instance of WebDriver, but at some point we will look into adding remote capabilities. 

That’s it for our DriverFactory class. Simple, right? Now we need a class to wrap our WebDriver code. In the next article, I will cover the DriverBase class, as well as expanding on some of the other partial classes we have in Driver.

As a challenge, why not add Firefox driver capability and some browser options