Parallel Execution
Run Selenium tests in parallel to dramatically reduce test execution time.
Selenium 3 & 4 Stable
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