Design Pattern – Factory

Design Pattern – Factory

Design Pattern - Factory

So far in our Design Pattern series, we have learnt about the Page Object Model and Facade and how they can be used within test automation. Both of which have their pros and cons, and both have situations where one is better suited than the other. In part three, we’re going to learn about another commonly used design pattern, the Factory design pattern.

Simply put, the factory design pattern allows your test, or code to determine what class or classes we will instantiate, postponing instantiation using logic often in the form of a switch statement.

A pure approach to the factory design pattern follows the GoF (Gang of Four) method which involves creating four classes to implement our factory. These are:

  • Product: defines the interface of objects the factory method creates. (e.g Clothes shop home page)
  • ConcreteProduct: implements the Product interface (e,g Ladies clothes home page, mens clothes home page)
  • Creator: declares the factory method, which returns an object of type Product.
  • ConcreteCreator: overrides the factory method to return an instance of a ConcreteProduct

So how might that look in code? Well let’s take the example we mentioned above with an online clothes shop. Depending on the type of clothes you are looking to buy will depend on the home page you see. Our Product class will contain a interface that serves as our product, in this case, our home page which would share common components. Our concrete products are the unique products that will implement our interface, in this case, both a mens home page and a ladies.

Our creator class is where we will class our logic that is used to determine which home page class we are going to instantiate. Finally our concrete creator, is where we implement our logic to let the code pick which class we need based on a parameter we will pass in to the method inside the class.

class ClothesShopHomePage
{
}
public class MensHomePage : ClothesShopHomePage
{
    public void WelcomeCustomer(ClothesShopHomePage homepage)
    {
    }
}

public class LadiesHomePage : ClothesShopHomePage
{
    public void WelcomeCustomer(ClothesShopHomePage homepage)
    {
    }
}

 public abstract class ClothesShopFactory
 {
 	public abstract ClothesShopHomePage GetHomepage(string homepageType);
 }
public class ConcreteClothesShopFactory : ClothesShopFactory
{
    public override ClothesShopHomePage GetHomepage(string homepageType)
    {
        ClothesShopHomePage homepage = null;

        switch(homepageType)
        {
            case "Mens":
                ClothesShopHomePage = new MensHomePage();
                break;
            case "Ladies":
                ClothesShopHomePage = new LadiesHomePage();
                break;
        }

        return ClothesShopHomePage;
    }
}

For the sake of saving you reading a lot of code that won’t really benefit your understanding, I have gone for a basic example of an interface and the classes implementing it. If you can imagine a test we want to write that would navigate to the home page, and select either male or female depending on the parameter we pass in to the test, which will then perform the core of the test once we have determined the correct home page object to create.

The core logic that will allow our test to instantiate the correct object is in our ConcreteClothesShopFactory class and the method inside GetHomePage (which is an overridden version of the GetHomePage method in our abstract class ClothesShopFactory. I appreciate that a lot of people reading this might be fairly new or inexperienced coders, so a lot of this is now starting to become a bit difficult to follow due to new concepts such as abstract classes, interfaces and overriding methods. Let’s take a step back.

Now, what I’m about to say next might seem a bit odd. But, stay with me. For the context of test automation, I want you to disregard our implementation of the factory design pattern we looked at above and forget some of those more advanced areas we looked at. Not because it’s wrong or we shouldn’t do it, far from it, in fact it was good that you saw that first as it gave you a good understanding of a factory, the principles and how it can be written. But with test automation, the way you’ll use it more often than not will be a lot more simple to implement than that. The logic is essentially the same, just depending on where we use it, we won’t need to create interfaces, creator classes and concrete creator classes.

In fact, if you’ve read the other content on my site, you’ll have seen that we’ve already been using a factory approach in our framework design in this article. But if you haven’t read it yet, let’s take a look at the code I’m referring to, which was in our Driver class:

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);
            }
        }
    }

You can see that to instantiate the browser driver that we want, we use a simple switch statement. This then instantiates a browser driver depending on the parameter passed in and this parameter comes in from another class. In a way, despite the more simple implementation, you can think of the method or class that calls this InitialiseDriver method as the Creator and the InitialiseDriver method itself as the Concrete Creator.

This is using the factory design pattern, and I’m betting that if you’ve written a Selenium framework before, or even just used one, you’ve seen something similar to the above example. It’s a very common use of this design pattern, and as mentioned earlier, despite its simple implementation, it’s still a perfectly valid example of a good way to use it.

How would we use this approach when it comes to our tests though? Surely it can’t still be that simple? Well I’m here to tell you that it’s even more simple, but no less effective.

[TestFixture]
public class MyFirstTest 
{
    [TestCase("Mens")
    [TestCase("Ladies")]
    public void UserCanNavigateToCorrectHomepage(string homepageType)
    {
        ClothesShopHomePage homepage = null;

        switch(homepageType)
        {
            case "Mens":
                homepage = new MensHomePage();
                break;
            case "Ladies":
                homepage = new LadiesHomePage();
                break;
        }

        Assert.IsTrue(homepage.IsUrlCorrect(), "Did not succesfully navigate to correct homepage")
    } 
}

As you can see, thanks to a simple Nunit attribute, we’re able to pass different parameter values to the same test meaning we can test both versions of our homepage in the same test. This means no code duplication for what would essentially be the same test, and instead we can use factory design principles to instantiate the correct class object depending on the scenario we’re testing.

I hope this has helped you understand how useful this design pattern can be, and how simple it can be to implement. It’s important to understand the theory behind it and familiarise yourself with the GoF method so you can appreciate the logic and the structure, even when using it in the simple examples we have. But if you were reading that and thinking it seems a bit above you, because of interfaces, abstract classes and so on, then fear not, because it isn’t necessarily required for the context of test automation.

As a final note, I’ll say that despite how effective it can be, it’s important not to overuse it. It can add unnecessary complexity to your code if not used in the correct scenario so be sure it’s a good time to use it. Some good times to use it would be:

  1. Tests need to figure out what objects should be created.
  2. Parent class allows later instantiation to subclasses means the creation of object is done when it is required.
  3. A class (creator) will not know what classes it will be required to create.