This article belongs to a series of 3 posts explaining how to use the Factory Pattern to create different browser instances for either local or remote execution using Selenium WebDriver.
You can find the following previous article:
This one will refer to some explanations and code from the previous one, but don’t be afraid, you will understand everything.
Can you imagine you can implement a good approach to create multiple browser instances with only two classes?
We already know that the Enum Types have constant and can have methods.
BrowserFactory class is an enum with the list of supported browsers (constants) for the implementation I’ve created: Chrome, Firefox, Edge, and Safari.
We can add an extra function on this enum with methods, but instead of creating the regular ones we will make each constant implement two methods:
createDriver() for the local execution and
getOptions() for the remote execution. In order to do so, we must implement abstract methods inside the enum. Take a look at the example:
On lines 23 and 24 we have two abstract methods. Each enum constant needs to implement the abstract method.
On lines 5 to 9, there’s the implementation of the createDriver() method creating a local browser instance using the WebDriverManager library. Note that the new browser instance is using the getOptions() and I will explain it in a minute.
You can see that line 24 has the AbstractDriverOptions with an unknown generic, placed with ? as the wildcard. It returns this class because each internal Selenium WebDriver remote browser implementation extends this class, making it easier to set the correct object for the remote instance creation. You can see this by accessing the AbstractDriverOptions Javadoc in the section “Direct Known Subclasses“
Lines 12 to 20 show the specific
ChromeOptions we must set for the remote execution, as the remote class is called “option” which must be set.
And why we have the
getOptions() method being used in the
createDriver()? To automatically set any option we want to. The option classes do not need the browser driver set as needed in the local execution.
The full BrowserDriver implementation can be found at https://github.com/eliasnogueira/selenium-java-browser-factory/blob/master/src/main/java/com/eliasnogueira/driver/BrowserFactory.java
This class is responsible to manage the local or remote instances because we need different code approaches using Selenium WebDriver. We have two main methods: the
createInstance() for the local execution and the
createRemoveInstance() for the remote execution.
The local instance means that we will run the tests locally in the browser installed on the machine.
createInstance() has the browser as the method parameter that the
BaseTest class will use to determine which browser will be instantiated.
The browser value attribute is transformed in the
Target enum and then used in the switch-case.
When the target is
BrowserFactory class will call the
createDriver() method for the browser we have set. This approach removes the necessity to have the
switch-case for each browser as the browser value will be transformed in the enum constant. Less code, more readable code!
You can see this happening on lines 8 to 10.
Remember that the browser to use locally is set in the general.properties class.
It works similar to the local approach when the code determines if the execution is local or remote. Line 12, in the previous code snippet, shows that the
BrowserFactory will call the
getOptions() method based on the browser we have set. With this, we have the browser options object. Now it’s time to create the real remote instance.
createRemoteInstance() method expects a
MutableCapability object, which is the same as the browser option object because the AbstractDriverOptions extends the MutableCapabilities class.
To create a remote browser instance we must use the
RemoteWebDriver class, adding as a parameter the target remote machine (or a grid) and the capability, which is the browser with the options.
On line 4 the URL for the remote machine is created based on the information we have in the grid.properties file. On line 6 the remote instance is created. The rest of the code is the exception handler, necessary to know what happened if any problem occurs during the remote instance creation.
First, it’s a good practice to have a BaseTest class managing the general pre and post-conditions. The BaseWeb class has the pre-conditions, as a JUnit @BeforeEach annotation, the following:
The main part related to this approach is in line 10, where the
DriverFactory class will know, internally, if the execution is local or remote based on the value set for the
target property set in the
general.properties file. The browser was also set in
general.properties but through the
browser property will be used to create the local browser instance or the remote instance.
The tests must extends the
BaseWeb to work properly.
If you would like to give it a try the complete project selenium-java-browser-factory has a docker-compose file that will start a Selenium 4 Grid with Chrome, Firefox, and Edge browsers. So you will be able to run remote instances.
To exercise this, please follow these steps:
http://localhost:4444to see the Selenium Grid page with the browsers available (Chrome, Edge, and Firefox)
general.propertiesfile, change the
targetattribute value to
src/main/java/resourcesalready have the values to connect to a local grid
You can navigate to the following GitHub repo to see a complete and functional example in the branch
local-remote-example. Spare some time to look at the new classes added and understand how it was structured.
If you have any questions related to the implementation, or if something is not clear, please leave a comment or send me a message on my social networks.