Thoughts on Selenium 101 Certification from LambdaTest
February 22, 2021Reasons to avoid RandomStringUtils for test data generation
October 10, 2021Introduction
We face one problem trying to apply the multi-browser test approach: the complexity of creating the browser instance.
In this article, I will show you the regular browser creation, how we can improve this, and a solution using the Factory design pattern.
The basic
Using Selenium WebDriver, we need to instantiate the browser class and inform its driver. The browser drivers are maintained as third-party plugins, and you can find them here. The only exception is the Safari driver.
Let’s say you want to run a test using Google Chrome. We have the following code:
public class GoogleChromeTest {
@Test
public void openPageUsingChrome() {
System.setProperty("webdriver.chrome.driver", "/path/to/chromedriver");
WebDriver driver = new ChromeDriver();
driver.get("https://eliasnogueira.com");
}
}
Using Firefox, we have the following:
public class FirefoxTest {
@Test
public void openPageUsingFirefox() {
System.setProperty("webdriver.gecko.driver", "/path/to/firefox");
WebDriver driver = new FirefoxDriver();
driver.get("https://eliasnogueira.com");
}
}
The problem with using this approach
There’s one main problem using this basic example wherein can be seen on line 5 in both: the addition of the driver path.
It is not wrong after all, it’s the first way to use it. The problem is that you will do either one or another (I also have seen both):
- adding the driver path in a generic folder
- adding the resource path and pushing the driver within your code
The second one is the worst approach ever from my perspective, so do not do that.
To avoid having these problems, I would strongly recommend you use the WebDriverManager library. I even have an article explaining a generic approach to applying the factory pattern using it.
The Factory design pattern
I have no intention to explain the Factory design pattern as we have tons of excellent articles and books. If you do not know this pattern, I would recommend reading this article.
The basic implementation is to have a class factory that will look into the browser you would like to create to run your tests and instantiate the browser with the configurations you want.
In the following diagram, the factory implementation is the DriverFactory
class the browser configuration is the classes with DriverManagr
suffix.
DriverFactory class
The DriverFactory
class has a method createInstance that receives the browser name as String to create the correct browser instance. We can instantiate the browser based on either an if-else statement or a switch case. To bring it more legibility, try to always go for the switch-case
.
public class DriverFactory {
public WebDriver createInstance(String browser) {
WebDriver driver;
BrowserList browserType = BrowserList.valueOf(browser.toUpperCase());
driver = switch (browserType) {
case CHROME -> new ChromeDriverManager().createDriver();
case FIREFOX -> new FirefoxDriverManager().createDriver();
case EDGE -> new EdgeDriverManager().createDriver();
case SAFARI -> new SafariDriverManager().createDriver();
};
return driver;
}
}
- Line 5 upper case the browser value to map it to the
BrowserList
enum t enable us to use it in theswitch-case
- Lines 8 to 11 create the driver instance based on the matching browser
Browser Manager classes
You noticed that the browser instance is using a custom class. We could directly use the driver class in the switch-case
, like this:
// previous code ignored
case CHROME:
driver = new ChromeDriver();
break;
Instead, we are using the ChromeDriverManager
that manages the browser driver using WebDriverManager to solve the problem mentioned at the beginning of this article and creates the browser instance.
public class ChromeDriverManager implements Factory {
@Override
public WebDriver createDriver() {
WebDriverManager.getInstance(CHROME).setup();
return new ChromeDriver();
}
}
It can be a better approach to keep your DriverFactory
class clean and with only one responsibility where the driver manager classes contain any additional code to each required browser as starting it maximized or changing any specific configuration.
Each driver manager class implements the Factory
interface to have a consistent implementation of the browser creation. When you open the other classes you will see the same method being used, but the different driver and browser usage.
How to use this approach
You might have a BaseTest class to manage the test pre and post-conditions. You need to inform the preconditions of the browser you need to use, passing it as a parameter to the DriverFactory class. The code would look like this:
public class BaseWeb {
protected WebDriver driver;
@BeforeEach
public void preCondition() {
String browserToUse = "chrome";
driver = new DriverFactory().createInstance(browserToUse);
driver.get("http:.//eliasnogueira.com");
}
@AfterEach
public void postCondition() {
driver.quit();
}
}
The BaseWeb
class above is an example to you understand how we can use it. I would not recommend you hardcode the browser. You can find a more elegant example of getting the browser from a config file by looking at this class.
A complete example
You can navigate to the following GitHub repo to see a complete and functional example like the one described in this article. I would recommend you read the README file to understand a little bit more about the architecture, libraries in use, and implementation.
https://github.com/eliasnogueira/selenium-java-browser-factory/tree/basic-example
What to do next?
Now it is your turn to get your hands dirty and try to create this approach. I would say to follow these steps:
- Create a
DriverFactory
class with an internal browser enumeration - Add the
switch-case
statement and add all the cases - Implement only one
case
to create a browser instance - Create a simple test class and use the
DriverFactory
method to create theWebDriver
instance with a hardcoded browser - If everything is working as expected, proceed to the next steps, otherwise review this article
- Create a browser manager class and refactor the
DriverFactory
class to use it - If it is still working, do the same for the other browser you would like to implement
Do not forget you can see an example in the mentioned GitHub repo.
Happy tests!
2 Comments
Code does not work.
There is no such method as “ChromeDriverManager().createDriver();”
Hi,
Would you mind sharing how are you creating/adopting the code?
I ran the test from the project example, and it’s working.
https://github.com/eliasnogueira/selenium-java-browser-factory/tree/basic-example