July 1, 2026
How to Run Selenium Tests in Parallel
Learn how to run Selenium tests in parallel locally and with Selenium Grid, plus common setup patterns, CI tips, and flakiness traps to avoid.
If you have a growing Selenium suite, serial execution eventually becomes the bottleneck that everyone feels. Developers wait longer for feedback, CI pipelines crawl, and a few slow end-to-end tests can hold up the entire team. The obvious answer is to run Selenium tests in parallel, but the practical version is more nuanced than just flipping a switch.
Parallelization is useful, but it also exposes shared-state problems, brittle test design, environment contention, and grid misconfiguration. I have seen teams cut runtime significantly and also teams create a flaky mess because they parallelized before the suite was ready.
This tutorial walks through how I approach parallel Selenium tests locally and with Selenium Grid, what to watch out for, and how to decide whether you should build and maintain this infrastructure yourself or use a managed platform such as Endtest, which offers a simpler alternative for teams that want parallel cloud execution without assembling all the pieces manually.
What parallel execution actually means
Parallel execution means running multiple test cases, classes, or browser sessions at the same time instead of one after another. The goal is shorter wall-clock time, not necessarily lower total CPU time. In practice, you usually parallelize at one of these levels:
- Test methods, where each test function runs independently
- Test classes, where each class gets its own browser session
- Browser sessions, where multiple WebDriver instances run against the same grid
- CI jobs, where your pipeline splits the suite into shards
Parallelization only works well when your tests are isolated. If two tests depend on the same user, the same order, or the same database row, parallel execution will reveal that design flaw immediately.
There are two broad scenarios:
- Local parallel execution, usually for developer machines or CI runners with multiple CPU cores.
- Selenium Grid parallel tests, where the browser sessions run on remote nodes and your runner distributes work across them.
The mechanics differ, but the engineering concerns are similar, isolation, thread safety, test data, and resource limits.
Before you parallelize, check whether the suite is ready
A fast flaky suite is still a flaky suite. Before I enable parallel Selenium tests, I check four things.
1. Tests must be independent
Each test should create and clean up its own data, or use an isolated namespace. Avoid shared users, shared carts, shared tenants, or a single reusable record that every test mutates.
Good patterns include:
- Create a unique user per test run
- Use API setup to seed data before browser steps
- Clean up with API calls or a database reset after the test
- Use test run IDs in entity names, emails, or order numbers
2. WebDriver instances must not be shared across threads
A WebDriver session is not something you safely reuse across parallel threads. Each parallel test needs its own driver instance. If you are using a base test class with a static driver, that is a common reason parallel runs fail in odd ways.
3. External dependencies can become the bottleneck
Parallel browser sessions can overload:
- Your application under test
- Your test database
- Authentication providers
- Third-party APIs
- Test fixtures and seed jobs
If the app slows down under load, parallelization may hide timing issues in one part of the suite while causing new timeout failures elsewhere.
4. Your waits need to be explicit
Parallel execution often makes timing issues more visible. Rely on explicit waits, not sleep calls. Selenium’s official documentation is the right place to revisit driver setup and waits if your suite still has a lot of hardcoded pauses, see the Selenium documentation.
How to run Selenium tests in parallel locally
Local parallelism is the easiest place to start. It gives you shorter feedback loops without introducing a grid or cloud provider. The exact implementation depends on the test framework, but the principle is the same, split the workload across worker threads or processes.
Example 1, pytest with Selenium in Python
If you use pytest, the most common approach is pytest-xdist, which runs tests across multiple workers.
pip install pytest pytest-xdist selenium
pytest -n auto
A simple Selenium fixture might look like this:
import pytest
from selenium import webdriver
@pytest.fixture def driver(): driver = webdriver.Chrome() yield driver driver.quit()
A test can stay clean and independent:
def test_login(driver):
driver.get("https://example.com/login")
assert "Login" in driver.title
The important part is that each worker gets its own browser session. Do not create one global driver and share it.
Example 2, JUnit with Maven Surefire
If you are on Java, Maven Surefire or Gradle can parallelize test execution at the framework level. In Maven, you might configure Surefire like this:
xml
That setting tells the runner to execute test classes in parallel. Again, each class should create its own WebDriver.
A thread-local driver pattern is often safer than a static driver:
public class DriverManager {
private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static WebDriver getDriver() {
return driver.get();
}
public static void setDriver(WebDriver webDriver) {
driver.set(webDriver);
}
public static void quitDriver() {
if (driver.get() != null) {
driver.get().quit();
driver.remove();
}
} }
This is not the only approach, but it illustrates the core idea, each thread owns its own browser session.
Local parallel execution tradeoffs
Local parallelism is simple, but it has limits:
- It competes with your laptop or CI runner CPU and memory
- Browser startup time can become noticeable
- Your suite may be faster, but not scalable beyond a few workers
- Debugging concurrent failures can be harder than serial runs
For small suites, local parallelization is usually enough. For larger browser matrices, you will eventually need a grid or remote execution strategy.
Running parallel Selenium tests with Selenium Grid
Selenium Grid is the native way to distribute WebDriver sessions across multiple machines or containers. The official Grid documentation is here, and it is worth reading before you stand up your own infrastructure: Selenium Grid.
Grid is a good fit when you need:
- Multiple browser sessions at once
- Browser version coverage across nodes
- A central endpoint for CI jobs
- More execution capacity than a single machine can provide
How Selenium Grid changes the architecture
With local execution, your test runner launches browsers on the same machine. With Grid, the runner connects to a remote endpoint, and the Grid routes sessions to available nodes.
A simplified setup looks like this:
- Test code creates a RemoteWebDriver
- The Grid hub or router receives the request
- A node with available capacity starts the browser session
- The test runs remotely and reports back to your runner
Example, Python with RemoteWebDriver
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
options = webdriver.ChromeOptions()
driver = webdriver.Remote( command_executor=”http://localhost:4444/wd/hub”, options=options, )
try: driver.get(“https://example.com”) assert “Example” in driver.title finally: driver.quit()
The URL depends on how your Grid is deployed. In modern Selenium Grid setups, the router endpoint is often exposed on port 4444.
Example, Java with RemoteWebDriver
ChromeOptions options = new ChromeOptions();
WebDriver driver = new RemoteWebDriver(
new URL("http://localhost:4444"),
options
);
This same pattern works in CI as long as the runner can reach the Grid.
Grid capacity planning matters
When teams ask why their parallel Selenium tests are still slow, the answer is often simple, they asked for more concurrency than the Grid can support.
Plan for:
- Number of parallel sessions
- Browser mix, Chrome, Firefox, Edge, Safari where applicable
- CPU and RAM per node
- Test duration variance
- Startup overhead for each browser session
A Grid with 4 nodes does not guarantee 4x speedup. If each node is already saturated, your new sessions will queue.
Docker Compose example for local Selenium Grid parallel tests
A lightweight way to experiment is to run Selenium Grid locally with Docker. This is useful for validating your parallel strategy before investing in a larger setup.
version: "3.8"
services:
selenium-hub:
image: selenium/hub:4.23.0
ports:
- "4444:4444"
chrome: image: selenium/node-chrome:4.23.0 shm_size: 2gb depends_on: - selenium-hub environment: - SE_EVENT_BUS_HOST=selenium-hub - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
For more serious use, you would add multiple nodes and possibly Firefox nodes too. The point here is not the exact version tag, it is the workflow, run a hub, add nodes, point your tests at the remote endpoint, and let the Grid manage concurrency.
How to split tests intelligently
Parallel execution is not just “run everything at once”. You still need to think about distribution.
By class or file
This is often the easiest starting point. Each test class or file is assigned to a worker. It is simple and predictable, but can be imbalanced if some files are much slower than others.
By suite tag
If you already tag tests by feature, smoke, regression, or critical path, you can run those groups in parallel jobs in CI.
By historical duration
The best balancing often comes from sharding based on past runtime. Longer tests get distributed so no single worker becomes the tail that drags the whole pipeline.
By browser matrix
A browser matrix is not the same thing as parallelization, but the two often get mixed together. If you want Chrome, Firefox, and Edge coverage, you can run those browser sets in parallel jobs as well. Just be careful not to multiply concurrency too aggressively.
The fastest suite is usually the one that does less work, then distributes that work cleanly.
CI/CD integration tips
Most teams want parallel Selenium execution in CI, not just on local machines. The easiest CI wins come from splitting the suite into jobs and keeping each job deterministic.
GitHub Actions example
name: selenium-tests
on: [push, pull_request]
jobs: ui-tests: runs-on: ubuntu-latest strategy: fail-fast: false matrix: shard: [1, 2, 3, 4] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: “3.11” - run: pip install -r requirements.txt - run: pytest -n auto –shard=$
This example assumes your test framework knows how to interpret shards. The main idea is to run multiple jobs in parallel, each with a defined slice of the suite.
CI pitfalls to avoid
- Reusing the same test user across jobs
- Writing to the same download path or screenshot path
- Using fixed ports for local services
- Letting one failed job cancel the rest when you need full signal
- Ignoring environment-specific browser startup cost
If your CI runners are underpowered, increasing test concurrency can make the pipeline slower or less stable. Measure before and after.
Common reasons parallel Selenium tests fail
When parallelization breaks, it usually does so in a few familiar ways.
Shared test data
Two tests create, update, or delete the same record. One passes, the other fails, or both become nondeterministic.
Unsafe globals
A shared config object, singleton, or static driver can leak state between workers.
Hard waits
Fixed sleeps are especially fragile when multiple browsers are contending for resources. Use explicit waits with reasonable timeouts.
Environment contention
If the app under test has rate limits or depends on third-party services, concurrent tests may trigger throttling.
Resource leaks
If a test fails before quit() is called, sessions can pile up. In parallel runs, that gets expensive fast.
A practical strategy for introducing parallelization
If I were enabling Selenium parallel execution on an existing suite, I would do it in this order:
- Fix the worst flaky tests first
- Remove obvious shared-state dependencies
- Make driver creation thread-safe
- Parallelize a small subset, usually smoke tests
- Add CI sharding or local worker parallelism
- Move to Grid if local capacity is not enough
- Measure runtime, flakiness, and resource usage after each change
This reduces the risk of turning a maintainable suite into a distributed debugging exercise.
When you should use Selenium Grid, and when you should not
Selenium Grid is worth it when your needs are clearly beyond one machine:
- Many browser sessions at once
- Cross-browser coverage at scale
- Shared remote infrastructure for multiple CI pipelines
- Longer-term control over browser node configuration
You may not need Grid if:
- Your suite is small
- You only need a few concurrent workers
- Your team does not want to own infrastructure
- You care more about speed of setup than fine-grained browser control
In those cases, a managed option can be attractive. For example, teams that want parallel cloud execution without building all the parallel Selenium infrastructure themselves sometimes look at Endtest vs Selenium. Endtest is an agentic AI Test automation platform with low-code and no-code workflows, so it can be a simpler alternative when the priority is to get reliable parallel runs without managing Grid nodes, browser capacity, and orchestration scripts. If you are migrating an existing suite, their Selenium migration docs are relevant too.
Debugging parallel failures
When a test fails only in parallel, I usually check these things first:
- Is the same data used by another worker?
- Is the browser session truly isolated?
- Are screenshots and downloads being written to unique paths?
- Is there a race condition in app setup or teardown?
- Is the Grid saturated or slow to allocate sessions?
A useful trick is to rerun the same failing slice serially. If it passes serially but fails in parallel, the issue is often shared state or environment pressure, not the browser action itself.
A few rules of thumb I trust
- Parallelize stable tests before unstable ones
- Prefer small, independent test cases over giant end-to-end flows
- Keep browser setup minimal so session startup cost stays manageable
- Isolate data by test run, not by manual cleanup hope
- Treat Grid capacity as a real resource, not an infinite queue
Final take
To run Selenium tests in parallel well, you need more than concurrency flags. You need isolation, thread-safe driver management, stable waits, CI awareness, and enough execution capacity to absorb the extra load. Start locally if you can, move to Selenium Grid when you need more scale, and keep a close eye on flakiness as soon as you multiply workers.
If your team wants the outcome, parallel browser execution, but not the overhead of building and maintaining the infrastructure, a platform like Endtest can be a reasonable alternative. For teams that are already deep into Selenium, though, a disciplined parallelization strategy is still a very practical way to reduce feedback time without rewriting the suite.