advanced20 min

Parallel Execution, Selenium Grid, and Remote WebDriver

Running many Selenium tests at once safely, and the Grid/RemoteWebDriver architecture that lets tests run against browsers on entirely different machines.

What you'll learn

  • Configure JUnit 5 parallel execution safely for a Selenium suite
  • Explain what Selenium Grid's hub/node architecture actually coordinates
  • Explain what RemoteWebDriver changes about how test code addresses the browser

Prerequisites

Explanation

JUnit 5's parallel execution (enabled via a junit-platform.properties file setting junit.jupiter.execution.parallel.enabled=true) is opt-in, unlike some testing frameworks where it's the default — a deliberate design choice, since Selenium tests each drive a genuinely expensive resource (a real browser process), and running many simultaneously without a per-test-fresh-driver discipline (this course's earlier lesson) would immediately reproduce exactly the state-leaking problems that discipline exists to prevent. Selenium tests are only safe to parallelize once each test genuinely creates and tears down its own driver instance — parallelizing a suite that shares driver instances across tests doesn't just risk flakiness, it actively breaks, since multiple threads would be issuing commands to the exact same browser session simultaneously.

Selenium Grid solves a different, related problem: running tests against many browser instances distributed across multiple machines, not just multiple threads on one machine. A Grid deployment has a hub (accepts incoming test session requests and routes them) and one or more nodes (each actually running browser instances) — a hub might route a Chrome-requesting session to a node that has Chrome available, and a Firefox-requesting session to a different node. This is the mechanism that lets a large organization run thousands of tests across a fleet of machines rather than being limited to whatever a single machine's CPU/memory can support running concurrently.

RemoteWebDriver (new RemoteWebDriver(new URL("http://grid-hub:4444"), new ChromeOptions())) is what test code uses to talk to a Grid hub instead of launching a browser locally — the crucial thing that does not change is everything else this course has covered: locators, waits, page objects, assertions all work completely identically, because RemoteWebDriver implements the exact same WebDriver interface as a local ChromeDriver. This is a genuinely important, honest point: writing tests that work correctly locally, then pointing them at a Grid by swapping only the driver-construction line, requires no other code changes at all, provided the tests were already written correctly against the standard WebDriver interface rather than accidentally depending on some local-machine-specific detail (a hard-coded local file path for upload, for instance, which would need to exist on whichever remote node actually runs that test).

Example

Modeling Grid's hub-routes-to-node architecture and RemoteWebDriver's interface-compatibility guarantee, as data.

function routeToNode(requestedBrowser, availableNodes) {
  const match = availableNodes.find((node) => node.browsers.includes(requestedBrowser));
  if (!match) throw new Error("no node available for " + requestedBrowser);
  return match.nodeId;
}

const nodes = [
  { nodeId: "node-1", browsers: ["chrome"] },
  { nodeId: "node-2", browsers: ["firefox", "webkit"] },
];
console.log(routeToNode("firefox", nodes)); // "node-2" -- the hub routes based on what's actually available

// The SAME test logic works identically whether "driver" is local or remote --
// this models WebDriver's shared interface across ChromeDriver and RemoteWebDriver.
function runTestLogic(driver) {
  driver.get("/login");
  driver.findElement("#username").sendKeys("alice");
  return "test logic ran identically, regardless of where the browser actually lives";
}
console.log(runTestLogic({ get: () => {}, findElement: () => ({ sendKeys: () => {} }) }));

Try it yourself

Call routeToNode for a browser ('safari') that no node supports, and confirm it correctly throws.

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Guided exercise

Guided exercise

Write routeToNode(requestedBrowser, availableNodes) modeling Grid's hub routing: find the first node whose 'browsers' array includes requestedBrowser, and return its nodeId; throw an Error if no node supports it.

Checks: routes to the correct node for a supported browser · throws when no node supports the requested browser

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Independent exercise

Independent exercise

Write isSafeToParallelize(testDesign) returning true only if testDesign.freshDriverPerTest is true AND testDesign.usesUniqueTestData is true AND testDesign.noSharedMutableState is true -- modeling the real preconditions a Selenium suite needs before parallel execution is actually safe, not just fast.

Checks: all three preconditions met is safe · a shared driver instance makes it unsafe · non-unique test data makes it unsafe

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Common mistakes

  • Enabling JUnit parallel execution on a suite that shares WebDriver instances across tests -- multiple threads issuing commands to the same browser session simultaneously doesn't just risk flakiness, it actively breaks in confusing ways.
  • Assuming Selenium Grid and parallel execution solve the same problem -- parallel execution runs multiple tests concurrently on ONE machine's resources; Grid distributes tests across MULTIPLE machines, and they're often used together, not as alternatives to each other.
  • Writing a test that depends on a local-machine-specific detail (a hard-coded local file path, a locally-installed certificate) and expecting it to work unchanged against RemoteWebDriver -- the remote node running that test may not have that same local resource available at all.

Knowledge check

Knowledge check

1. Why is JUnit parallel execution opt-in rather than a default for Selenium test suites?
2. What does Selenium Grid's hub actually coordinate?
3. What must change in test code to point already-working tests at a Selenium Grid via RemoteWebDriver?

Takeaway

Parallel execution is only safe once each test genuinely owns a fresh driver instance, no shared mutable state, and unique test data; Grid distributes browser sessions across multiple machines via a hub/node architecture; and RemoteWebDriver requires no test-code changes beyond driver construction, since it implements the identical WebDriver interface.

Summary

JUnit parallel execution is opt-in and only safe with a fresh-driver-per-test, unique-data, no-shared-state design. Selenium Grid's hub routes incoming session requests to nodes hosting the requested browser, distributing load across machines. RemoteWebDriver implements the same WebDriver interface as a local driver, so tests work unchanged beyond the driver-construction line.

References

Your notes

Notes save automatically.