ScreenshotNeo

BlogHow-to

How to Speed Up Selenium Test Execution

Reduce Selenium suite wall time by removing unnecessary waits, tuning navigation, and adding measured parallelism without sacrificing reliability.

By the ScreenshotNeo team4 October 20268 min read

The most reliable way to speed up Selenium tests is to first remove time spent waiting without purpose, then add concurrency where tests and infrastructure can support it. Replace fixed sleeps with waits for specific conditions, check whether navigation must wait for every asset, and enable runner parallelism gradually. Use Selenium Grid when you need remote sessions or broader browser and operating system coverage. Measure suite duration, stability, and resource use before and after each change; there is no universal safe thread count or guaranteed speedup.

1. Measure a representative baseline

Before changing the suite, record a representative run in the environment you intend to optimize. Keep the same test selection, browser versions, machine capacity, and application conditions when comparing results.

  • Record wall-clock suite duration, not only the sum of individual test durations.
  • Record failures, retries, and flaky outcomes alongside duration.
  • Observe CPU and memory use on test runners and browser hosts.
  • Note the number of browser sessions and whether tests share accounts, records, or other state.

Make one change at a time where practical. If duration stops improving or failures rise as concurrency increases, resource pressure or shared-state contention may be the limiting factor. Selenium’s Grid guidance gives illustrative arithmetic for understanding distribution, not a benchmark or a promise for a particular suite. See When to Use Grid and Grid sizing guidance.

2. Replace fixed sleeps with condition-based waits

A fixed sleep pauses for its full duration whether the page is ready immediately or takes longer than expected. This wastes time in fast cases and can still fail in slow cases. Wait for the condition the next test action actually requires, such as an element becoming visible or clickable.

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

# Wait only until the action is safe to perform.
submit = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, "button[type='submit']"))
)
submit.click()

# For an assertion, wait for the expected page state.
WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.ID, "confirmation"))
)

Choose a timeout appropriate to the application’s expected behavior and report a clear failure when it expires. Avoid sleeps as a general synchronization mechanism; reserve a short deliberate delay for cases where a specific condition cannot be observed. Selenium warns: “Do not mix implicit and explicit waits.” Combining them can make actual wait times unpredictable. Follow the Selenium waiting strategies guidance and use a consistent wait approach.

3. Tune navigation waiting to what the test needs

WebDriver’s default page load strategy, normal, waits for the document readiness state complete. Depending on the application, that can include waiting for assets the test does not need. Selenium also supports eager, which waits until interactive, and none, which does not wait for a ready-state value. A faster return from navigation is safe only when the test synchronizes on the state it needs before interacting.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.page_load_strategy = "eager"  # normal, eager, or none

driver = webdriver.Chrome(options=options)
try:
    driver.get("https://example.com")
    # Navigation returning does not prove a dynamic app is ready.
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.support.ui import WebDriverWait

    WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, "main"))
    )
finally:
    driver.quit()

Evaluate eager if the test needs the DOM but can proceed without late-loading images or other resources. Use none only when the test has deliberate synchronization after navigation. Dynamic applications may render content after the document reaches interactive; condition-based waits remain necessary. See Selenium’s browser options documentation.

4. Run Selenium tests in parallel with the test runner

Parallel execution can reduce elapsed time when tests are independent and the browser sessions, application, and test data can handle concurrent work. It can also expose hidden coupling: tests that edit the same record or rely on shared browser state may fail when run together. Begin with a conservative level of concurrency and validate isolation before increasing it.

JUnit Jupiter

JUnit Jupiter is sequential by default; parallel execution is opt-in. Add a junit-platform.properties file to the test resources with settings such as:

junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.mode.default = concurrent
junit.jupiter.execution.parallel.mode.classes.default = concurrent
junit.jupiter.execution.parallel.config.strategy = fixed
junit.jupiter.execution.parallel.config.fixed.parallelism = 4

The value 4 is an example configuration, not a recommended universal thread count. Confirm the exact settings against the JUnit version in your project. Keep tests that share unsafe state isolated or configured for same-thread execution. Consult the official JUnit parallel execution guide.

TestNG

TestNG supports parallel modes including methods, tests, classes, and instances, with a configurable thread count. For example, a suite can run test methods concurrently:

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
<suite name="Parallel suite" parallel="methods" thread-count="4">
  <test name="Browser checks">
    <classes>
      <class name="example.CheckoutTest"/>
    </classes>
  </test>
</suite>

Again, 4 is illustrative. Select a mode that matches how tests share fixtures and state. Check the TestNG documentation for version-specific configuration and lifecycle behavior.

5. Use Selenium Grid when sessions need more capacity or coverage

Runner parallelism controls concurrency in the test suite. Selenium Grid provides remote browser sessions and can distribute them across machines, browser types, versions, and operating systems. Grid and runner-level parallelism can be used together: the runner requests concurrent sessions and Grid routes them to available capacity.

Do not infer capacity from a universal formula. Selenium’s getting-started guide offers roughly one CPU and one gigabyte of RAM per browser as a reference point, while explicitly advising teams to measure performance and noting defaults may not suit every context. Browser choice, operating system coverage, machine count, memory, CPU, and desired concurrent sessions all matter. Start with capacity you can observe, then increase it while watching resource use and test stability. See Grid sizing guidance and Grid applicability.

6. Compare changes without trading away reliability

Repeat the representative run after each meaningful change and compare it with the baseline. Track at least these measures:

Measure What it helps reveal
Wall-clock duration Whether users of the suite actually get results sooner
Failures and retries Whether speed came with flakiness or contention
CPU and memory Whether browsers or runners are saturated
Session queueing Whether concurrency requests exceed available browser capacity

Selenium explains the rough relationship as Number of Tests * Average Test Time / Number of Nodes = Total Execution Time in an illustrative Grid calculation. Real suites have setup, queueing, uneven test duration, shared services, and resource limits, so treat the relation as intuition rather than a forecast. If more sessions no longer reduce elapsed time, investigate saturation and shared state before adding capacity.

7. Troubleshooting common slow or flaky suites

Symptom Likely cause What to try
Suite spends long stretches idle Fixed sleeps or overly broad waits Wait for the specific visible, clickable, or completed state needed by the next action.
Waits take longer than their configured timeout Implicit and explicit waits are combined Remove the mixed wait configuration and follow one consistent strategy.
Tests fail after switching to eager or none Navigation returned before the application state needed by the test Add an explicit condition for that state, or return to normal if the test depends on complete loading.
Failures appear only in parallel runs Tests share data, accounts, files, or mutable application state Isolate test data and resources; serialize tests that cannot safely run concurrently.
More threads make the suite slower CPU, RAM, browser host, application, or Grid capacity is saturated Inspect resource use and session queueing; lower concurrency or add measured capacity.
Grid sessions remain queued or fail to start Requested browser capabilities have no available matching capacity Check node availability, browser/platform coverage, and configured session limits.
Local runs improve but CI does not Different runner resources, contention, or browser setup in CI Measure in the CI environment and tune for its actual capacity rather than extrapolating local results.

Or skip the browser setup

For screenshot checks and page captures, ScreenshotNeo is a website screenshot API and MCP server. A single GET request returns a PNG, JPEG, WebP, or PDF. It is useful when a check needs a captured page image rather than Selenium-driven interaction. It does not replace Selenium for functional tests that click through workflows or assert application behavior.

Cookie banners are accepted and 60+ known consent platforms, newsletter popups, and chat widgets are removed before the shot; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

Set YOUR_API_KEY to your key. The API also supports full-page and selector captures, viewport and device options, waits, custom headers and cookies, caching, async jobs, bulk capture, signed image links, and PDF options; use the docs for parameter names and supported values. Sign up for 1,000 free screenshots a month with no card.

FAQ

How many parallel sessions should I use?

There is no universally safe number. Start below the capacity you expect, then measure duration, failures, CPU, memory, and queued sessions while increasing concurrency.

Should I use runner parallelism or Grid?

Runner parallelism enables concurrent tests. Grid supplies remote browser sessions and machine or platform distribution. Use Grid when local capacity or required browser coverage calls for it; they can be combined.

Does eager always make tests faster?

No. It may return sooner when late assets are irrelevant, but tests still need to wait for the application state they use. The correct strategy depends on the page and test.

Can Selenium guarantee a well-architected suite?

No. Selenium’s test practices documentation notes that its tools make functional user interaction easier but do not ensure suite architecture. Isolation, synchronization, and measurement remain suite design responsibilities: Selenium test practices.