Parallel Execution
Run Selenium tests in parallel to dramatically reduce test execution time.
Selenium 3 & 4 Stable Verified against Selenium 4.48.0
Running tests in parallel can reduce execution time from hours to minutes. This guide covers parallel execution strategies for each language and framework.
Why Parallel Execution?
| Sequential (10 tests × 1 min) | Parallel (5 threads) |
|---|---|
| 10 minutes total | ~2 minutes total |
Thread-Safe WebDriver
The key to parallel execution is ensuring each test has its own WebDriver instance:
Thread-Safe Driver Factory
Selenium 3 & 4 Stable
import org.openqa.selenium.WebDriver;import org.openqa.selenium.chrome.ChromeDriver;
public class DriverFactory { // ThreadLocal ensures each thread gets its own driver private static ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static WebDriver getDriver() { if (driver.get() == null) { driver.set(new ChromeDriver()); } return driver.get(); }
public static void quitDriver() { if (driver.get() != null) { driver.get().quit(); driver.remove(); // Important: remove from ThreadLocal } }}import threadingfrom selenium import webdriver
class DriverFactory: _driver = threading.local()
@classmethod def get_driver(cls): if not hasattr(cls._driver, 'instance') or cls._driver.instance is None: cls._driver.instance = webdriver.Chrome() return cls._driver.instance
@classmethod def quit_driver(cls): if hasattr(cls._driver, 'instance') and cls._driver.instance: cls._driver.instance.quit() cls._driver.instance = None// JavaScript with async/await handles this naturally// Each test file runs in its own context with Mocha parallel mode
const { Builder } = require('selenium-webdriver');
async function createDriver() { // Each call creates a new driver return await new Builder().forBrowser('chrome').build();}
module.exports = { createDriver };using OpenQA.Selenium;using OpenQA.Selenium.Chrome;using System.Threading;
public class DriverFactory{ // ThreadLocal ensures each thread gets its own driver private static ThreadLocal<IWebDriver> _driver = new ThreadLocal<IWebDriver>();
public static IWebDriver GetDriver() { if (_driver.Value == null) { _driver.Value = new ChromeDriver(); } return _driver.Value; }
public static void QuitDriver() { if (_driver.Value != null) { _driver.Value.Quit(); _driver.Value = null; } }}TestNG Parallel Execution (Java)
XML Configuration
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<!-- Run test methods in parallel --><suite name="Parallel Suite" parallel="methods" thread-count="5"> <test name="All Tests"> <classes> <class name="tests.LoginTest"/> <class name="tests.SearchTest"/> <class name="tests.CheckoutTest"/> </classes> </test></suite>
<!-- Or run test classes in parallel --><suite name="Parallel Suite" parallel="classes" thread-count="3"> <test name="All Tests"> <classes> <class name="tests.LoginTest"/> <class name="tests.SearchTest"/> <class name="tests.CheckoutTest"/> </classes> </test></suite>
<!-- Or run tests (groups of classes) in parallel --><suite name="Parallel Suite" parallel="tests" thread-count="2"> <test name="Smoke Tests"> <classes> <class name="tests.SmokeTest"/> </classes> </test> <test name="Regression Tests"> <classes> <class name="tests.RegressionTest"/> </classes> </test></suite>Base Test with ThreadLocal
import org.openqa.selenium.WebDriver;import org.testng.annotations.*;
public class BaseTest { protected WebDriver driver;
@BeforeMethod public void setup() { driver = DriverFactory.getDriver(); driver.manage().window().maximize(); }
@AfterMethod public void teardown() { DriverFactory.quitDriver(); }}
// Tests extend BaseTestpublic class LoginTest extends BaseTest {
@Test public void testLogin() { driver.get("https://example.com/login"); // Each thread has its own driver }
@Test public void testLogout() { driver.get("https://example.com"); // Independent of other tests }}pytest-xdist (Python)
Installation and Usage
pip install pytest-xdist
# Run with 4 workerspytest -n 4
# Auto-detect CPU countpytest -n auto
# Distribute tests across workerspytest -n 4 --dist=loadfile # Same file on same workerpytest -n 4 --dist=loadscope # Same class/module on same workerThread-Safe Fixtures
import pytestfrom selenium import webdriver
@pytest.fixture(scope="function")def driver(): """Each test gets its own driver instance.""" driver = webdriver.Chrome() driver.maximize_window() yield driver driver.quit()
# For session-scoped fixtures with parallel, use file locking@pytest.fixture(scope="session")def shared_resource(tmp_path_factory, worker_id): if worker_id == "master": # Not running in parallel return setup_resource()
# Running in parallel - use file locking root_tmp_dir = tmp_path_factory.getbasetemp().parent lock_file = root_tmp_dir / "resource.lock"
with FileLock(str(lock_file)): return setup_resource()JUnit 5 Parallel (Java)
Configuration
junit.jupiter.execution.parallel.enabled=truejunit.jupiter.execution.parallel.mode.default=concurrentjunit.jupiter.execution.parallel.mode.classes.default=concurrentjunit.jupiter.execution.parallel.config.strategy=fixedjunit.jupiter.execution.parallel.config.fixed.parallelism=4Thread-Safe Tests
import org.junit.jupiter.api.*;import org.junit.jupiter.api.parallel.Execution;import org.junit.jupiter.api.parallel.ExecutionMode;
@Execution(ExecutionMode.CONCURRENT) // Enable parallel for this classclass ParallelTest {
@BeforeEach void setup() { // Each test gets its own driver via ThreadLocal DriverFactory.getDriver().manage().window().maximize(); }
@Test void testOne() { WebDriver driver = DriverFactory.getDriver(); driver.get("https://example.com/page1"); }
@Test void testTwo() { WebDriver driver = DriverFactory.getDriver(); driver.get("https://example.com/page2"); }
@AfterEach void teardown() { DriverFactory.quitDriver(); }}Mocha Parallel (JavaScript)
Configuration
module.exports = { parallel: true, jobs: 4, // Number of parallel jobs timeout: 30000};
// Or via command line// npx mocha --parallel --jobs 4Test Structure
// Each test file runs in its own processconst { Builder, By } = require('selenium-webdriver');
describe('Login Tests', function() { let driver;
beforeEach(async function() { driver = await new Builder().forBrowser('chrome').build(); });
afterEach(async function() { await driver.quit(); });
it('should login successfully', async function() { await driver.get('https://example.com/login'); // Test logic });});NUnit Parallel (C#)
Assembly-Level Parallelism
// In AssemblyInfo.cs or any file[assembly: Parallelizable(ParallelScope.Fixtures)]
// Or per class[TestFixture][Parallelizable(ParallelScope.All)]public class ParallelTests{ private IWebDriver driver;
[SetUp] public void Setup() { driver = DriverFactory.GetDriver(); }
[Test] public void TestOne() { driver.Navigate().GoToUrl("https://example.com"); }
[Test] public void TestTwo() { driver.Navigate().GoToUrl("https://example.com/page2"); }
[TearDown] public void Teardown() { DriverFactory.QuitDriver(); }}Best Practices
1. Test Independence
# BAD - Tests depend on each otherdef test_create_user(driver): # Creates user "john" pass
def test_login_as_user(driver): # Assumes "john" exists - fails in parallel! pass
# GOOD - Each test is independentdef test_login(driver): user = create_test_user() # Create fresh data login(driver, user) delete_test_user(user) # Cleanup2. Unique Test Data
// Generate unique data per testString uniqueEmail = "user_" + UUID.randomUUID() + "@test.com";String uniqueUsername = "user_" + System.currentTimeMillis();3. Resource Isolation
# Each test uses its own database record@pytest.fixturedef test_product(db): product = db.create_product(name=f"Product-{uuid.uuid4()}") yield product db.delete_product(product.id)Performance Comparison
| Approach | 100 Tests | Complexity |
|---|---|---|
| Sequential | 50 min | Low |
| 4 Parallel | 13 min | Medium |
| 8 Parallel | 7 min | Medium |
| Grid (20 nodes) | 3 min | High |
Common Issues
| Issue | Solution |
|---|---|
| Port conflicts | Use dynamic ports or grid |
| Shared state | Use ThreadLocal/fixtures |
| Flaky tests | Ensure test independence |
| Resource limits | Limit thread count |
Next Steps
- Selenium Grid - Distributed execution
- Headless Browsers - Faster execution
- Data-Driven Testing - Test data isolation
- pytest Integration - Python parallel setup
- TestNG and JUnit - Java parallel setup