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.
The 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.
The method 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 LOCAL
the 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.
The 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:
docker-compose up
commandhttp://localhost:4444
to see the Selenium Grid page with the browsers available (Chrome, Edge, and Firefox)general.properties
file, change the target
attribute value to remote
grid.properties
file in src/main/java/resources
already have the values to connect to a local gridBookARoomWebTest
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.
https://github.com/eliasnogueira/selenium-java-browser-factory/tree/basic-example
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.