Factory Method Pattern in the creation of Selenium WebDriver

As software engineer, We all face some errors/exceptions while writing code! So what do we do when we face such a problem? If we are not sure, We google for solutions immediately. Don’t we? We google because we know that we would not be alone and someone would have already found the solution, for the problem we are facing now, which we could use to solve our problem.

Well, What to do if we face an issue in the high level software design – when connecting different classes / modules – when you have only a vague idea!! How can we google such things when we ourselves are not sure of the problem we have in the first place!!

No worries, You still have a solution!

Design patterns are the solutions for the problems which you might face in the software design!!

Design Patterns are well optimized and reusable solutions to a given problem in the software design. It helps us to show the relationship among the classes and the way in which they interact. A design pattern is a template that you have to carefully analyze and use inappropriate places.

If you are interested to find out more about design patterns, additional information can be found here.

Factory Method:

Factory Pattern is one of the creation Patterns. It is mostly used when we need to create an object from one of several possible classes that share a common super class/implements an interface. It creates objects without exposing the instantiation logic to the user. We, as the user, refer to the newly created object through a common interface.

A good example is – WebDriver

Factory Pattern- Selenium WebDriver
Factory Pattern- Selenium WebDriver

As all these Chrome/Firefox/Safari/IE driver concrete classes implement this WebDriver interface, We are able to refer to the ChromeDriver, FirefoxDriver and so on, instance through the WebDriver interface without much change in the code.

Problem in Creating WebDriver Instance:

Even though the above concrete classes implements a common interface, We need to follow different approaches in creating an instance from one of these concrete classes which depends on the browser instance we might want to use.

For ex: ChromeDriver requires a Driver Server setup but Firefox (till version 45) does not need anything.

WebDriver driver = new FirefoxDriver();

The above code simply works – but the below code might not unless we set driver server executable.

WebDriver driver = new ChromeDriver();

Proposal solution:

In our case we defined suppliers for each type of browser that is going to be used in our testing app and base on a parameter that is sent on runner the correct browser is initialized and used during test execution. Full code of

WebDriverFactory is presented bellow: 
@Component
public class WebDriverFactory {

    @Autowired
    private ScenarioContext scenarioContext;

    protected static WebDriver driver;

    private static final Map<DriverType, Supplier<WebDriver>> driverMap = new HashMap<>();

    private static final Map<DriverType, Supplier<WebDriver>> driverMapLinux = new HashMap<>();

    private static final Supplier<WebDriver> chromeDriverSuplier = () -> {
        WebDriverManager.chromedriver().setup();
        return new ChromeDriver();
    };

    private static final Supplier<WebDriver> firefoxDriverSuplier = () -> {
        WebDriverManager.firefoxdriver().setup();
        System.setProperty(FirefoxDriver.SystemProperty.BROWSER_LOGFILE, "/dev/null");
        return new FirefoxDriver();
    };

    private static final Supplier<WebDriver> headLessChromeDriver = () -> {
        WebDriverManager.chromedriver().setup();
        ChromeOptions chromeOptions = new ChromeOptions();
        chromeOptions.addArguments("--headless");
        return new ChromeDriver(chromeOptions);
    };

    private static final Supplier<WebDriver> headLessFirefox = () -> {
        WebDriverManager.firefoxdriver().setup();
        System.setProperty(FirefoxDriver.SystemProperty.BROWSER_LOGFILE, "/dev/null");
        FirefoxBinary firefoxBinary = new FirefoxBinary();
        firefoxBinary.addCommandLineOptions("--headless");
        FirefoxOptions firefoxOptions = new FirefoxOptions();
        firefoxOptions.setBinary(firefoxBinary);
        return new FirefoxDriver(firefoxOptions);
    };
    private static final Supplier<WebDriver> internetExplorerDriver = () -> {
        WebDriverManager.iedriver().setup();
        return new InternetExplorerDriver();
    };

    private static final Supplier<WebDriver> chromeDriverSuplierLinux = () -> {
        WebDriverManager.chromedriver().setup();
        return new ChromeDriver();
    };

    private static final Supplier<WebDriver> firefoxDriverSuplierLinux = () -> {
        WebDriverManager.firefoxdriver().setup();
        System.setProperty(FirefoxDriver.SystemProperty.BROWSER_LOGFILE, "/dev/null");
        return new FirefoxDriver();
    };

    private static final Supplier<WebDriver> headLessChromeDriverLinux = () -> {
        WebDriverManager.chromedriver().setup();
        ChromeOptions chromeOptions = new ChromeOptions();
        chromeOptions.addArguments("--headless");
        return new ChromeDriver(chromeOptions);
    };


    private static final Supplier<WebDriver> headLessFirefoxLinux = () -> {
        WebDriverManager.firefoxdriver().setup();
        System.setProperty(FirefoxDriver.SystemProperty.BROWSER_LOGFILE, "/dev/null");
        FirefoxBinary firefoxBinary = new FirefoxBinary();
        firefoxBinary.addCommandLineOptions("--headless");
        FirefoxOptions firefoxOptions = new FirefoxOptions();
        firefoxOptions.setBinary(firefoxBinary);
        return new FirefoxDriver(firefoxOptions);
    };

    static {
        driverMap.put(DriverType.CHROME, chromeDriverSuplier);
        driverMap.put(DriverType.FIREFOX, firefoxDriverSuplier);
        driverMap.put(DriverType.CHHEADLESS, headLessChromeDriver);
        driverMap.put(DriverType.IE, internetExplorerDriver);
        driverMap.put(DriverType.FXHEADLESS, headLessFirefox);
        driverMapLinux.put(DriverType.CHROME, chromeDriverSuplierLinux);
        driverMapLinux.put(DriverType.FIREFOX, firefoxDriverSuplierLinux);
        driverMapLinux.put(DriverType.CHHEADLESS, headLessChromeDriverLinux);
        driverMapLinux.put(DriverType.FXHEADLESS, headLessFirefoxLinux);
    }

    private static WebDriver getDriverManager(DriverType driverType) {
        return driverMap.get(driverType).get();
    }

    private static final WebDriver getDriver() {
        String driverType;
        if (driver == null) {
            try {
                driverType = System.getProperty("browser").toUpperCase();
                driver = getDriverManager(DriverType.valueOf(driverType));
            } catch (NullPointerException e) {
                driver = getDriverManager(DriverType.FIREFOX);
            }
        }
        return driver;
    }

    private static WebDriver getDriverManagerLinux(DriverType driverType) {
        return driverMapLinux.get(driverType).get();
    }

    private static final WebDriver getDriverLinux() {
        String driverType;
        if (driver == null) {
            try {
                driverType = System.getProperty("browser").toUpperCase();
                driver = getDriverManagerLinux(DriverType.valueOf(driverType));
            } catch (NullPointerException e) {
                driver = getDriverManagerLinux(DriverType.FIREFOX);
            }
        }
        return driver;
    }

    public WebDriver setUp() {

        try {
            if (System.getProperty("os").equals("linux")) {
                driver = getDriverLinux();
            } else {
                driver = getDriver();
            }
        } catch (NullPointerException e) {
            driver = getDriver();
        }

        driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
        Dimension dimension = new Dimension(1500, 1980);
        driver.manage().window().maximize();
        return driver;
    }

    public void closeDriver() {
        driver = (WebDriver) scenarioContext.getData(PageKeys.OPEN_DRIVER);
        driver.quit();
        driver = null;
    }
}

Project example could be found on here.