Selenium Parallel Execution

A regression suite that takes three hours sequentially can often finish in under an hour when tests run in parallel. This guide covers how to run Selenium tests in parallel with TestNG, the one change that makes parallel runs safe (a ThreadLocal WebDriver), how Selenium Grid 4 spreads tests across browsers and machines, and how to set up cross-browser runs.

Advertisement

What Is Parallel Testing?

Running several tests at the same time instead of one after another. With TestNG you choose what runs in parallel (methods, classes, test blocks) and how many threads; with Selenium Grid you choose where the browsers run.

Benefits and costs

  • Faster feedback — runtime drops roughly in proportion to the number of threads, until machine resources run out.

  • Cross-browser coverage in the same time as a single-browser run.

  • But tests must be independent — no shared WebDriver, no shared test data, no dependency on execution order. Most "parallel problems" are really test-design problems.

TestNG Parallel Modes

The parallel attribute (on <suite> or <test>) accepts:

Value What runs in parallel Typical use
methods Every @Test method in its own thread Maximum speed; needs fully independent tests
classes Each class in its own thread; methods in a class run sequentially A safe middle ground
tests Each <test> block in its own thread Cross-browser: one <test> per browser ⭐
instances Different instances of the same class in different threads (methods of one instance share a thread) Factory-created test instances
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Regression" parallel="classes" thread-count="4">
  <test name="All tests">
    <classes>
      <class name="com.example.tests.LoginTest"/>
      <class name="com.example.tests.CartTest"/>
      <class name="com.example.tests.CheckoutTest"/>
    </classes>
  </test>
</suite>

thread-count sets the maximum number of threads (TestNG's default is 5). Data providers can also run rows in parallel with @DataProvider(parallel = true).

More: TestNG Essentials.

Make It Thread-Safe: One Driver per Thread ⭐

A single static WebDriver shared by all tests is the #1 cause of broken parallel runs — threads click in each other's browsers and tests fail randomly. Give each thread its own driver:

public final class DriverFactory {
    private static final ThreadLocal<WebDriver> DRIVER = new ThreadLocal<>();

    public static void init(String browser) {
        WebDriver driver = switch (browser.toLowerCase()) {
            case "firefox" -> new FirefoxDriver();
            case "edge"    -> new EdgeDriver();
            default        -> new ChromeDriver();
        };
        DRIVER.set(driver);
    }

    public static WebDriver get() { return DRIVER.get(); }

    public static void quit() {
        WebDriver driver = DRIVER.get();
        if (driver != null) {
            driver.quit();
            DRIVER.remove();     // avoid leaks when TestNG reuses threads
        }
    }
}

public class BaseTest {
    @Parameters("browser")
    @BeforeMethod
    public void setUp(@Optional("chrome") String browser) { DriverFactory.init(browser); }

    @AfterMethod(alwaysRun = true)
    public void tearDown() { DriverFactory.quit(); }
}

Page objects receive DriverFactory.get() instead of a shared field. Also make test data unique per test (for example, generated emails) so parallel tests don't collide. Since Selenium 4.6, Selenium Manager downloads drivers automatically — no System.setProperty needed.

Cross-Browser Testing

Run the same tests on several browsers by passing the browser as a parameter — one <test> block per browser, running in parallel:

<suite name="Cross-browser" parallel="tests" thread-count="3">
  <test name="Chrome">
    <parameter name="browser" value="chrome"/>
    <classes><class name="com.example.tests.LoginTest"/></classes>
  </test>

  <test name="Firefox">
    <parameter name="browser" value="firefox"/>
    <classes><class name="com.example.tests.LoginTest"/></classes>
  </test>

  <test name="Edge">
    <parameter name="browser" value="edge"/>
    <classes><class name="com.example.tests.LoginTest"/></classes>
  </test>
</suite>

The @Parameters("browser") in BaseTest picks up each block's value. In CI, run headless with a fixed window size — see Test Passes Locally, Fails in Jenkins.

What Is Selenium Grid?

Selenium Grid runs tests on browsers on other machines or containers. Your tests connect to one URL; the Grid routes each session to a node that has the requested browser and platform. That gives you:

  • Parallel execution across many machines, not just threads on one.
  • Cross-browser and cross-platform coverage (Windows, Linux, macOS browsers).
  • Scalability — add nodes to add capacity.
  • Consistent environments, especially with Docker images.

Selenium Grid 4 components

Selenium 3 Grid had a simple hub and nodes. Grid 4 was rebuilt with separate internal components — router, distributor, session map, session queue and event bus — and you can run it three ways:

Mode What it is When to use
Standalone Everything in one process on one machine Learning, local runs, small CI setups
Hub and node A hub (router, distributor, etc. together) plus nodes on other machines Most teams' own Grid
Distributed Each component runs separately Large-scale, high-availability grids
# Standalone (download selenium-server-<version>.jar from selenium.dev)
java -jar selenium-server-<version>.jar standalone

# Hub and node
java -jar selenium-server-<version>.jar hub
java -jar selenium-server-<version>.jar node --hub http://<hub-ip>:4444

# Or with Docker — the quickest way
docker run -d -p 4444:4444 --shm-size="2g" selenium/standalone-chrome

Open http://localhost:4444/ui to see nodes and running sessions.

Full Docker setup, including multiple browsers: Docker + Selenium Grid Guide.

Connecting a test to the Grid

ChromeOptions options = new ChromeOptions();

WebDriver driver = new RemoteWebDriver(
    URI.create("http://localhost:4444").toURL(),
    options
);

Use FirefoxOptions or EdgeOptions for other browsers — the options object tells the Grid which browser you need. In a framework, the driver factory decides between local drivers and RemoteWebDriver from configuration (for example -Dgrid.url=…).

Cloud providers such as BrowserStack and Sauce Labs expose the same WebDriver endpoint for real devices and more browser versions.

Parallel-Run Checklist

  1. One WebDriver per thread (ThreadLocal) — never a shared static driver.
  2. Independent tests — no order dependencies or dependsOnMethods chains.
  3. Unique or isolated test data per test.
  4. Explicit waits only — parallel runs make timing issues more visible.
  5. Thread-safe reporting (Extent/Allure listeners that handle concurrency).
  6. A thread count your machine or Grid can actually support.

See how WebDriver commands flow to the browser in the Selenium WebDriver Visualizer.

From Real Projects

On my projects I performed batch, group, parallel and cross-browser execution of Selenium scripts with TestNG, on Chrome and Firefox. Parallel runs are where hidden dependencies show up — shared data or a shared browser session between tests — so tests need to be independent before you speed them up. On Canolog, the many forms and screens across sales, inventory, finance and service are where keeping page details in POM classes and reusable steps in a business library paid off. Make every test independent — its own data and its own browser session — before running in parallel.

FAQs

What is parallel testing in Selenium?

Running several tests at the same time — usually with TestNG threads, and across machines with Selenium Grid — to reduce total execution time.

What values can TestNG's parallel attribute take?

methods, classes, tests and instances — they control what unit runs in parallel.

What is the default thread count in TestNG?

  1. Set it with the thread-count attribute.

Why do tests fail randomly when run in parallel?

Usually a shared static WebDriver or shared test data. Use ThreadLocal<WebDriver> and independent data.

What is Selenium Grid?

A server that routes WebDriver sessions to browsers on other machines or containers, enabling distributed, parallel and cross-browser execution.

What changed in Selenium Grid 4?

It was rewritten with separate components (router, distributor, session map, session queue, event bus), can run standalone, as hub and node or fully distributed, has a built-in UI and good Docker support.

How do you perform cross-browser testing?

Pass the browser as a parameter (one <test> per browser in testng.xml), create the matching driver in a factory, and run the blocks in parallel — locally or on a Grid.