ScreenshotNeo

BlogGuides

Selenium WebDriver Tutorial for Cross-Browser Testing

Build reliable Selenium WebDriver tests across browsers and operating systems. Set up Python, choose a test matrix, and scale runs with Selenium Grid.

By the ScreenshotNeo team4 October 20269 min read

To use Selenium WebDriver for cross-browser testing, write one test around a user-visible workflow, run it against a deliberately chosen set of browsers and operating systems, and compare the results. Locally, install a Selenium language binding and the browsers you want to test; Selenium Manager handles driver management by default in current bindings. Use Selenium Grid and RemoteWebDriver when you need remote machines, more browser versions, or parallel sessions. This tutorial uses Python; the setup and browser-specific details differ by binding.

Selenium WebDriver is the browser automation interface and its browser-control implementations. It drives browsers locally or remotely, and WebDriver is a W3C Recommendation. A browser’s driver and capabilities still matter, so “the same test” does not mean every browser behaves identically.

1. Choose a browser and platform matrix

Decide which environments matter before building infrastructure. Start with your product’s supported environments and the workflows where browser differences carry real risk. A compact matrix is easier to maintain and interpret than an indiscriminate list of every possible combination.

Dimension What to choose What to record when a test fails
Browser family For example, Chrome, Firefox, Safari, or Edge, according to your support commitments. Browser name and relevant capabilities.
Browser version A current supported version, plus older versions if your users or support policy require them. Exact browser version and driver or remote node details.
Operating system Operating systems your product supports or users depend on. Operating system and version.
Concurrency How many sessions your team needs to run at once to meet turnaround goals. Whether the failure is reproducible at that concurrency.

Keep the user workflow and assertions comparable across runs. Add browser-specific options only when needed, and record the requested and actual capabilities alongside failures. Expand the matrix in response to product support needs and observed risk.

2. Install Selenium and run a local test

The local setup consists of a Selenium binding, a browser, and a driver implementation. Selenium Manager is the default driver and browser management route in Selenium bindings; follow the current setup guide for your language and environment rather than copying stale driver-download steps. Selenium documents browser-specific behavior for Chrome, Edge, Firefox, Internet Explorer, and Safari. Check the live browser documentation for the browser and version you use.

For Python, install the binding:

python -m pip install selenium

Save this as smoke_test.py. It opens a public page, checks its title, and closes the session even if an assertion or browser command fails:

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait


def main():
    driver = webdriver.Chrome()
    try:
        driver.get("https://example.com")
        WebDriverWait(driver, 10).until(
            lambda browser: browser.title == "Example Domain"
        )
        assert driver.find_element("css selector", "h1").text == "Example Domain"
        print({"title": driver.title, "url": driver.current_url})
    finally:
        driver.quit()


if __name__ == "__main__":
    main()

Run it with python smoke_test.py. The example uses Chrome and Selenium Manager. Install the browser first, and check the current Selenium Python installation guide if your environment needs extra setup.

3. Make assertions wait for the page

Browser navigation finishing does not guarantee that an application’s asynchronous content is ready. Wait for the state your assertion needs, such as an element becoming visible, a URL changing, or a result message appearing. Explicit waits make the condition visible in the test and avoid relying on a guessed delay.

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

wait = WebDriverWait(driver, 10)
result = wait.until(
    EC.visibility_of_element_located((By.CSS_SELECTOR, "[data-testid='search-result']"))
)
assert "Result" in result.text

Use stable selectors owned by the application, such as accessible roles, labels, or dedicated test attributes. Avoid locating elements by fragile layout structure when the test is meant to verify user behavior. Do not mix implicit and explicit waits without understanding their interaction; Selenium’s waiting strategy documentation describes the available approaches.

4. Run the same workflow across browsers

Parameterize which browser starts while keeping the test intent and assertions stable. This small Python example selects Chrome or Firefox from an environment variable:

import os
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait

browser_name = os.environ.get("BROWSER", "chrome").lower()

if browser_name == "chrome":
    driver = webdriver.Chrome()
elif browser_name == "firefox":
    driver = webdriver.Firefox()
else:
    raise ValueError(f"Unsupported BROWSER={browser_name!r}")

try:
    driver.get("https://example.com")
    WebDriverWait(driver, 10).until(lambda browser: browser.title)
    assert driver.find_element("css selector", "h1").text == "Example Domain"
finally:
    driver.quit()

Run it with BROWSER=chrome python smoke_test.py or BROWSER=firefox python smoke_test.py in a POSIX shell. In CI, use the equivalent environment-variable syntax for that runner. Extend the mapping with browsers your team supports and keep browser-specific configuration close to browser construction.

Compatibility details can change. Selenium’s Chrome documentation says Chrome and ChromeDriver major versions must match; Selenium Manager can manage drivers for local bindings, but remote nodes still need a usable browser and driver setup. If a result differs, reproduce it on the named browser version and operating system, then check capabilities and environment before attributing the difference to your application. See Selenium’s browser-specific documentation.

5. Use Selenium Grid for remote and parallel runs

Use local sessions for initial development. Choose Grid when you need remote machines, a broader browser and operating-system matrix, or more simultaneous sessions. Grid routes WebDriver commands to remote browser instances. A client connects with RemoteWebDriver, a Grid address, and browser options that identify the requested browser. Selenium’s Grid guide covers running across browser types, versions, operating systems, and machines.

For a simple local Grid, install Java 11 or higher, obtain the Selenium Server JAR from the official Selenium downloads, and start Standalone mode:

java -jar selenium-server-<version>.jar standalone

Standalone combines Grid components on one machine and listens at http://localhost:4444 by default. Confirm the server is ready at http://localhost:4444/status. Replace <version> with the JAR version you downloaded.

Here is a Python remote session against that Grid:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait

options = Options()
driver = webdriver.Remote(
    command_executor="http://localhost:4444",
    options=options,
)
try:
    driver.get("https://example.com")
    WebDriverWait(driver, 10).until(lambda browser: browser.title)
    assert driver.title == "Example Domain"
finally:
    driver.quit()

The browser requested by the options must be available on a Grid node. For a Hub and Node arrangement, start the Hub and register Nodes using the Grid’s current instructions; this lets you combine machines with different systems or browser versions. Use the Hub address as the client’s Grid URL. For a fully distributed setup, use the Router address. Consult the current Grid getting-started guide for ports and component configuration.

Grid capabilities and session matching

Browser options identify the requested browser and can include standard capabilities such as browserVersion, platformName, page-load strategy, and certificate handling. Some capabilities are browser-specific. On a remote Grid, version and platform requests help route the session to a matching node; a request cannot succeed if no node offers compatible capabilities. Selenium 4 uses browser options classes, and a remote session requires an options instance.

Use only capabilities your Grid or browser supports. A requested browser version is not proof that the session ran that version: inspect the actual session and node details. Avoid enabling acceptance of invalid TLS certificates except where the test environment specifically requires it.

6. Keep tests reliable and useful

  • Wait for observable states: wait for the relevant element, text, URL, or other condition, with a timeout that fits the application.
  • Keep setup and cleanup explicit: create a session per test or test unit as your runner’s isolation model requires, and always quit it.
  • Make failures diagnosable: capture the browser, version, platform, requested capabilities, test name, and failure details.
  • Separate environment failures from product failures: check browser availability, driver compatibility, Grid health, and node capacity when session creation or navigation fails.
  • Control test data: isolate accounts and state where parallel tests could interfere with each other.
  • Handle remote files deliberately: a path on the client machine is not automatically present on the remote browser machine. Selenium documents additional handling for remote uploads and managed downloads.

7. Performance, reliability, and cost

Grid can reduce turnaround by running sessions concurrently, but the achievable concurrency depends on available machines and their CPU, memory, browser versions, and workload. Selenium explicitly treats sizing figures as environment-dependent guidance, not a universal capacity promise. Start with measured session behavior in your own environment, then increase concurrency while watching queue time, failures, and resource use.

More matrix entries and more parallel sessions consume more compute and increase the number of environments to maintain. Keep a small high-value matrix on every change if that fits your delivery needs, and run broader coverage on a schedule or for higher-risk changes. This is a planning approach, not a Selenium guarantee. For reliability, keep the Grid reachable only by trusted clients: Selenium warns that an externally exposed Grid can let third parties access infrastructure, internal applications, or run custom binaries. Use appropriate network controls.

8. Troubleshooting common failures

Symptom Likely cause Fix
Driver cannot start or browser session creation fails locally Browser is missing, driver is unavailable, or the browser and driver are incompatible. Install the browser, check the current browser-specific Selenium guidance, and let Selenium Manager manage the local driver where supported. For Chrome, verify matching major versions.
Remote session request times out or is rejected Grid is not ready or reachable, the URL is wrong, or no node matches requested options. Check /status, confirm the Grid URL and port, inspect registered nodes, and align the options with capabilities actually available.
Element lookup fails immediately The element has not appeared yet, the selector is incorrect, or the page is in a different state. Verify the selector and page state, then wait for the expected condition rather than adding an arbitrary sleep.
Test passes in one browser and fails in another Browser behavior, version, platform, or browser-specific configuration differs. Reproduce with the exact browser, version, OS, and capabilities. Check whether the assertion depends on a browser-specific behavior or timing assumption.
Remote file upload cannot find a path The path is on the client machine but the browser runs on a remote node. Use Selenium’s remote upload support or make the file available to the remote environment using the Grid’s supported mechanism.
Grid starts but sessions are slow or queue Requested concurrency exceeds available node capacity, or browsers contend for resources. Measure session duration and resource use, reduce concurrency, or add appropriate node capacity. Grid sizing depends on the environment.
A test leaves browsers running Cleanup did not run after an assertion or exception. Put driver.quit() in a finally block or use the equivalent teardown hook in your test framework.

Or skip the browser setup

If the task is to capture a page rather than interact with it as part of a test, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. See the API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Cookie banners are accepted and removed before capture, along with known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers identify the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Create a free account for 1,000 screenshots a month, with no card.

FAQ

Does one WebDriver test prove a site works in every browser?

No. It checks the selected workflow in the browser and environment where it ran. Choose coverage that reflects your support commitments and risk.

Should every test run on every browser and operating system?

Not necessarily. Use an explicit matrix, then expand it when user coverage, product support, or observed failures justify the added maintenance and compute.

When should I move from local WebDriver to Grid?

Use Grid when you need remote browser environments, multiple machines, or parallel execution. Local sessions remain useful for developing and debugging tests.

Can Selenium WebDriver test mobile apps?

This guide covers browser automation for web applications. Choose tooling and a test setup specifically suited to the mobile browser or native application coverage you require.

Primary references