The previous tutorial explained running the tests in Selenium4 Grid using the Standalone option. One of the major advantages of Selenium Grid is the ability to run tests parallelly on multiple browsers simultaneously. Parallel testing helps to reduce execution time and efforts and results in a faster time to delivery.
In this tutorial, we will run the same set of tests on Chrome, Firefox, and Edge browsers sequentially initially to confirm that we can perform Cross Browser testing also
It is very important to construct our tests thread-safe in order to run them in parallel without a problem.
This example uses Selenium 4 with TestNG.
Implementation Steps
1. Download Selenium Grid 4
The latest version of Selenium 4 is 4.3.0 and the same can be downloaded from the official website of Selenium.
2. Download various Browser driver exe
It is recommended to download the exe of various browsers in the same location where the Selenium WebDriver jar file is present. This is because Selenium 4 Alpha has the ability to automatically detect the WebDrivers present on the node machine. For this example, I have downloaded Chrome, Firefox, and edge drivers.

3. Start Selenium Server Jar
Open a command line terminal. Go to the location where these files are present
cd C:/Users/Vibha/Software/SeleniumGrid
Use the below command to run selenium-server standalone jar files.
java -jar selenium-server-4.3.0.jar standalone --port 4445
It’s optional to mention the port number at the end of the command. By default, it will use port 4444. It is good practice to mention the port at the end to avoid any conflict between the ports.

4. Open Selenium Console
The server is listening on http://172.30.96.1:4445/ui/index.html which is the same as shown in the above image. This is a dynamic address, so make sure to get the address from the logs when Selenium Grid is started.
The Grid automatically identifies that the WebDrivers for Chrome and Firefox are present on the system.

5. Add dependencies to the project
In this example, we are using a Maven project, so are adding the dependencies to the POM.xml.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Selenium4Parallel</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!-- Selenium 4 Dependency -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.1.2</version>
</dependency>
<!-- Selenium WebDriver Manager -->
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>5.1.0</version>
</dependency>
<!-- TestNG Dependency -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.4.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
6. Create a Test Code
Creating an instance of the Remote WebDriver and passing the selenium endpoint and chrome options defined in it.
To run a Remote WebDriver client, we first need to connect to the RemoteWebDriver. We do this by pointing the URL to the address of the server running our tests. In order to customize our configuration, we set desired capabilities.
Below is an example of instantiating a remote WebDriver object pointing to our remote web server running our tests on Chrome, Firefox, and Edge. We have used @Parameters Annotation to pass the browser names to the tests.
import org.openqa.selenium.By;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Parameters;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
public class BaseTest {
protected static ThreadLocal<RemoteWebDriver> driver = new ThreadLocal<RemoteWebDriver>();
public static String remote_url = "http://localhost:4445/";
public Capabilities capabilities;
@Parameters({"browser"})
@BeforeMethod
public void setDriver(String browser) throws MalformedURLException {
System.out.println("Test is running on "+browser);
if(browser.equals("firefox")) {
capabilities = new FirefoxOptions();
} else if (browser.equals("chrome")) {
capabilities = new ChromeOptions();
} else if (browser.equals("edge")) {
capabilities = new EdgeOptions();
}
driver.set(new RemoteWebDriver(new URL(remote_url), capabilities));
driver.get().get("https://opensource-demo.orangehrmlive.com/");
driver.get().manage().window().maximize();
driver.get().manage().timeouts().pageLoadTimeout(Duration.ofSeconds(10));
WebDriverWait wait = new WebDriverWait(driver.get(), Duration.ofSeconds(10));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='divUsername']/span")));
}
public WebDriver getDriver() {
return driver.get();
}
@AfterMethod
public void closeBrowser() {
driver.get().quit();
driver.remove();
}
}
Selenium4ParallelDemo
This class contains the various tests that need to be executed.
- In BaseTest class, I created ThreadLocal <>() webdriver (ThreadLocalMap) for thread-safe test execution
- I got the TestNG parameter (browser) with @Parameter annotation.
- BaseTest returns browser Capabilities based on browser name.
- In BaseTest class, the getDriver() method returns the created driver.
- Selenium4ParallelDemo class extends TestBase class and comprises their test code.
import org.openqa.selenium.By;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
public class Selenium4ParallelDemo extends BaseTest {
@Test
public void validCredentials() {
getDriver().findElement(By.xpath("//*[@id='txtUsername']")).sendKeys("Admin");
getDriver().findElement(By.xpath("//*[@id='txtPassword']")).sendKeys("admin123");
getDriver().findElement(By.xpath("//*[@id='btnLogin']")).click();
String newPageText = getDriver().findElement(By.xpath("//*[@id='content']/div/div[1]/h1")).getText();
System.out.println("newPageText :" + newPageText);
assertEquals(newPageText,"Dashboard");
}
@Test
public void invalidCredentials() {
getDriver().findElement(By.xpath("//*[@id='txtUsername']")).sendKeys("1234");
getDriver().findElement(By.xpath("//*[@id='txtPassword']")).sendKeys("12342");
getDriver().findElement(By.xpath("//*[@id='btnLogin']")).click();
String actualErrorMessage = getDriver().findElement(By.xpath("//*[@id='spanMessage']")).getText();
System.out.println("Actual ErrorMessage :" + actualErrorMessage);
assertEquals(actualErrorMessage,"Invalid credentials");
}
@Test
public void loginPageHeading() {
String loginText = getDriver().findElement(By.xpath("//*[@id='logInPanelHeading']")).getText();
System.out.println("Actual loginText :" + loginText);
assertEquals(loginText,"LOGIN Panel");
}
@Test
public void forgotPasswordPageHeading() {
getDriver().findElement(By.xpath("//*[@id='forgotPasswordLink']/a")).click();
String forgetPasswordTitle= getDriver().findElement(By.xpath(" //*[@id='content']/div[1]/div[2]/h1")).getText();
System.out.println("Actual Page Title of Forgot Password Page :" + forgetPasswordTitle);
assertEquals(forgetPasswordTitle,"Forgot Your Password?");
}
@Test
public void verifyLinkedIn() {
Boolean linkedInIcon = getDriver().findElement(By.xpath("//*[@id='social-icons']/a[1]/img")).isEnabled();
System.out.println("Actual linkedIn Text :" + linkedInIcon);
assertTrue(linkedInIcon);
}
}
7. Create a testng.xml
It is very easy to create testng.xml in the case of Eclipse. Right-click on the project, and select TestNG -> Convert to TestNG. It will create a basic testng.xml structure. We need to add the parameter name and value to it.
We are planning to run the test on 3 different browsers, so we are passing the name of the browser from this testng.xml using the parameter. This testng.xml will run the test sequentially. There is a minor change needs to be done in testng.xml that will run the tests parallelly.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test thread-count="5" name="Chrome Test">
<parameter name ="browser" value="chrome"/>
<classes>
<class name="org.example.Selenium4ParallelDemo"/>
</classes>
</test> <!-- Test -->
<test thread-count="5" name="Firefox Test">
<parameter name ="browser" value="firefox"/>
<classes>
<class name="org.example.Selenium4ParallelDemo"/>
</classes>
</test> <!-- Test -->
<test thread-count="5" name="Edge Test">
<parameter name ="browser" value="edge"/>
<classes>
<class name="org.example.Selenium4ParallelDemo"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
8. Run the tests from testng.xml
Right-Click on the testng.xml and select Run As ->TestNG Suite.

9. Navigate to the sessions tab on the Selenium Grid UI upon running the command
It would reflect an active session.

10. Review the test execution result
There are 2 ways to see if the tests are getting executed or not. First, we can check in the command line. We can see the logs there as shown below.

The complete test execution result can be found in the console too.

The tests are TestNG ones, so we can also check the “Result of Running Suite“ tab also.

11. TestNG Report Generation
TestNG generates the test reports in the test-output folder.

We are interested in 2 reports – index.html and emailable-report.html.
Index.html

The below image shows that the tests are run sequentially.

Emailable-Report.html
This report is the summary report. It contains the summary of all the tests executed like, the number of tests passed, skipped, retried, and failed, and the execution time taken by each test.

Parallel Testing
Update the testng.xml for parallel testing. Add parallel=”tests” in the XML.
Updated testng.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite" parallel="tests">
<test thread-count="5" name="Chrome Test">
<parameter name ="browser" value="chrome"/>
<classes>
<class name="org.example.Selenium4ParallelDemo"/>
</classes>
</test> <!-- Test -->
<test thread-count="5" name="Firefox Test">
<parameter name ="browser" value="firefox"/>
<classes>
<class name="org.example.Selenium4ParallelDemo"/>
</classes>
</test> <!-- Test -->
<test thread-count="5" name="Edge Test">
<parameter name ="browser" value="edge"/>
<classes>
<class name="org.example.Selenium4ParallelDemo"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
The below image shows that the tests were running on multiple browsers at the same time.

The console also shows that tests were started on all three browsers at the same time.

Index.html shows that methods run in chronological order. It can be seen that the setDriver() method for all 3 browsers was the first one to be executed.

Best Practices:
- It is advisable to use nodes other than 4444 to run the tests. By using the different port numbers, we prevent the risk that the port is already in use on your system.
- It is suggested to use RemoteWebDriver objects, as it is used to set which node (or machine) our test will run against.
Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!