How to Build and Run Selenium Projects
Build a repeatable Selenium project, run your first browser test, understand driver setup, and decide when to use Selenium Grid.
Selenium projects combine a language binding, a browser, and a browser-specific WebDriver implementation. Add the binding to your project, write a script or test that starts a browser session and closes it, then run it with your language’s test runner. For many local setups, Selenium Manager finds or downloads the needed driver automatically. Add Selenium Grid when you need remote or distributed browser runs.
This guide starts with a complete Python project and then shows the same setup choices for Java and JavaScript. Selenium’s getting-started guide explains that WebDriver is a language-neutral interface and that browser drivers handle communication with their browsers.
1. Choose a language and project shape
A Selenium project has three main parts:
- Language binding: the library your code imports, such as Python’s
seleniumpackage or Java’sselenium-java. - Browser: Chrome, Firefox, Edge, or another browser supported by the binding and driver.
- Browser driver: the browser-specific WebDriver implementation that receives commands from Selenium and controls the browser.
For a small project, keep the structure simple. Put dependencies in a manifest or build file so another developer or CI runner can recreate the environment. Start with one local browser and your language’s normal test runner. Add remote execution only when you need it.
selenium-project/
├── requirements.txt
├── tests/
│ └── test_homepage.py
└── README.md
2. Build a runnable Python Selenium project
Python is a concise way to get a first Selenium test running. This example uses pytest, starts Chrome, checks a page title, and always closes the browser—even if an assertion fails.
Create and install the project dependencies
mkdir selenium-project
cd selenium-project
python -m venv .venv
Activate the environment, then create requirements.txt:
# macOS or Linux
source .venv/bin/activate
# Windows PowerShell
.venv\Scripts\Activate.ps1
selenium
pytest
Install the dependencies:
python -m pip install -r requirements.txt
For a repeatable team or CI setup, pin reviewed versions in the dependency file and update them deliberately. The version shown in any copied example can become stale; check current package and runtime compatibility when selecting pins.
Write and run the test
Create tests/test_homepage.py:
from selenium import webdriver
def test_selenium_homepage_title():
driver = webdriver.Chrome()
try:
driver.get("https://www.selenium.dev/")
assert "Selenium" in driver.title
finally:
driver.quit()
Run just this test or the whole test directory:
pytest -q tests/test_homepage.py
pytest -q
webdriver.Chrome() creates a local browser session. Selenium Manager is commonly invoked when the binding has not been given an explicit driver. driver.quit() ends the session and releases the browser process; put it in cleanup code so failures do not leave browsers running.
3. Install Selenium in other language ecosystems
Use the package manager and test runner that fit the project. The exact runtime and dependency versions move over time, so consult Selenium’s current library installation guide when setting up a new project.
Java with Maven and JUnit
For Maven, add Selenium and JUnit to pom.xml. This minimal project targets Java 11 or later, consistent with the current Grid quick-start requirement; verify your selected Selenium release’s requirements.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example</groupId>
<artifactId>selenium-project</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.release>11</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.49.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.12.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
</plugin>
</plugins>
</build>
</project>
Versions above are example pins; update them intentionally after checking the current Selenium and JUnit documentation. Create src/test/java/example/HomepageTest.java:
package example;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
class HomepageTest {
@Test
void titleContainsSelenium() {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://www.selenium.dev/");
assertTrue(driver.getTitle().contains("Selenium"));
} finally {
driver.quit();
}
}
}
Run the test suite from the directory containing pom.xml:
mvn clean test
Java with Gradle
For Gradle, declare Selenium as a test dependency and use JUnit Platform. In build.gradle:
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.seleniumhq.selenium:selenium-java:4.49.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.12.2'
}
test {
useJUnitPlatform()
}
Use the same JUnit test class, under src/test/java, and run:
./gradlew test
# If the project does not include the Gradle wrapper:
gradle test
JavaScript with Node.js
Install the binding and a test runner:
mkdir selenium-js
cd selenium-js
npm init -y
npm install selenium-webdriver
npm install --save-dev mocha
Add a test command to package.json:
{
"scripts": {
"test": "mocha \"test/**/*.spec.js\" --timeout 120000"
}
}
Create test/homepage.spec.js:
const { Builder } = require('selenium-webdriver');
const assert = require('node:assert/strict');
describe('homepage', function () {
this.timeout(120000);
it('has a Selenium title', async function () {
const driver = await new Builder().forBrowser('chrome').build();
try {
await driver.get('https://www.selenium.dev/');
const title = await driver.getTitle();
assert.ok(title.includes('Selenium'));
} finally {
await driver.quit();
}
});
});
Run it with:
npm test
Selenium also documents setup for .NET and Ruby. Use its current installation page for their package references and supported runtime details rather than copying a potentially outdated version number.
4. Do you need ChromeDriver or another driver?
Usually, you can try the current Selenium binding with a locally installed supported browser first. Selenium Manager has shipped with Selenium since version 4.6.0 and is used by bindings as a fallback when a driver has not been provided. Starting with Selenium 4.11.0, it also gained managed browser discovery, download, and caching for Chrome, Firefox, and Edge. See the official Selenium Manager documentation for current behavior and platform limits.
| Setup route | Use it when | Trade-off |
|---|---|---|
| Selenium Manager | You have a supported platform and ordinary local development or CI setup. | It may need network access to discover or obtain compatible components; first startup may take longer. |
| Explicit driver management | Your network is restricted, architecture is unsupported, or the environment requires controlled browser and driver versions. | You own version matching, installation, updates, and path configuration. |
| Remote WebDriver / Grid | The browser runs on another machine, or tests need multiple environments or distributed execution. | You must operate or obtain a reachable remote WebDriver endpoint and manage its capacity. |
A fixed local driver path can work, but hard-coding it into shared test code ties that code to one machine. Prefer environment-specific configuration or Selenium Manager unless your deployment requires an explicit path. Selenium lists Linux arm64/aarch64, 32-bit architectures, and Raspberry Pi among Selenium Manager limitations; check the current docs and the browser/driver compatibility guidance before choosing a manual route. See Selenium’s browser driver guidance.
5. Make browser tests reliable
Most flaky tests come from timing assumptions or cleanup gaps. Use explicit waits for the condition your next action depends on, use stable locators, and close each session in a guaranteed cleanup path.
- Wait for conditions: avoid arbitrary sleeps where a DOM or state condition can be waited for. Selenium’s waiting strategies explain implicit and explicit waits. Avoid mixing wait styles without understanding how their timeouts interact.
- Use stable selectors: prefer stable IDs, accessible attributes, or dedicated test attributes over selectors tied to incidental layout.
- Keep tests isolated: tests should not depend on browser state left by another test. Create and close sessions consistently.
- Set useful timeouts: page-load and script timeouts should reflect application behavior. A larger timeout does not fix a broken condition; capture enough context to diagnose where the wait stalled.
- Keep browser versions deliberate in CI: automatic discovery is convenient, while reproducible environments may require controlled browser and driver versions.
6. Run tests locally, then use Selenium Grid when needed
Local execution is the simplest starting point. Use pytest, Maven, Gradle, or your JavaScript test runner to run the suite on the developer’s machine or a CI worker. Selenium Grid routes WebDriver commands to remote browser instances and supports parallel runs across machines, browser versions, and operating systems.
Start a local standalone Grid
- Install Java 11 or higher and the browsers you plan to run.
- Download the Selenium Server JAR from the official Selenium downloads page.
- Start standalone mode:
java -jar selenium-server-<version>.jar standalone
Standalone listens for WebDriver requests at http://localhost:4444 by default. The Grid guide says drivers can be available on PATH, or Selenium Manager can configure them with --selenium-manager true. Point your test at the remote endpoint instead of starting a local driver.
Python example using Grid
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
driver = webdriver.Remote(
command_executor="http://localhost:4444",
options=options,
)
try:
driver.get("https://www.selenium.dev/")
print(driver.title)
finally:
driver.quit()
JavaScript example using Grid
const { Builder } = require('selenium-webdriver');
const driver = await new Builder()
.usingServer('http://localhost:4444')
.forBrowser('chrome')
.build();
try {
await driver.get('https://www.selenium.dev/');
console.log(await driver.getTitle());
} finally {
await driver.quit();
}
For separate Hub and Node machines, networking and Grid component configuration matter: the Hub and Nodes need to reach one another, including the Event Bus ports when using defaults. Grid’s components, roles, and configuration are documented in the Grid quick start. Protect Grid from external access with appropriate network controls; an exposed endpoint lets outside parties request browser sessions.
7. Run and diagnose the project
Run tests from the project root so the test runner finds its configuration and dependencies:
| Project | Focused run | Full run |
|---|---|---|
| Python / pytest | pytest -q tests/test_homepage.py |
pytest -q |
| Java / Maven | mvn -Dtest=HomepageTest test |
mvn clean test |
| Java / Gradle | ./gradlew test --tests '*HomepageTest' |
./gradlew test |
| JavaScript / Mocha | npx mocha test/homepage.spec.js |
npm test |
8. Common Selenium setup errors
| Symptom | Likely cause | What to do |
|---|---|---|
| Driver executable cannot be found or session creation fails. | Browser/driver setup is missing, incompatible, or Selenium Manager cannot access what it needs. | Confirm the browser is installed, update Selenium, inspect Selenium Manager output, and check platform/network support. If needed, install a compatible driver and configure it for that environment. |
| Browser and driver versions do not match. | A manually installed driver or pinned browser is incompatible with the other component. | Align browser and driver versions using official browser/driver guidance, or remove the stale manual path and let Selenium Manager resolve the setup where supported. |
| Package import or class cannot be found. | Dependency installed into a different Python environment, omitted from a Java build file, or not installed by npm. | Activate the intended virtual environment; rerun pip install -r requirements.txt, Maven/Gradle dependency resolution, or npm install from the project root. |
| Connection refused at Grid endpoint. | Grid is stopped, listening at a different address or port, or blocked by network configuration. | Start the server, confirm the configured endpoint, and check connectivity to the Grid host and port. For remote Nodes, check Hub, Node, and Event Bus reachability. |
| Grid reports no matching browser slot. | No Node has registered a browser that satisfies the requested capabilities. | Check Node logs and available browsers, then make requested browser/version/platform capabilities match a registered slot. |
| Test intermittently cannot find an element. | The page has not reached the expected state, a locator is unstable, or the element is inside a frame or different browsing context. | Wait for the relevant condition, verify the locator and page state, and switch to the correct frame or window when required. |
| Browser processes remain after a failure. | The test exited before quit() ran. |
Put teardown in finally or the test framework’s guaranteed teardown hook. |
| CI run hangs or runs out of resources. | Too many concurrent browser sessions, a process leak, or resource-heavy pages. | Close every session, reduce parallelism, and allocate capacity based on observed CPU and memory use. |
9. Performance, reliability, and cost
Selenium itself is a library and browser automation stack; the main runtime cost is usually the browser processes and infrastructure that execute the tests. Parallel sessions can shorten suite wall time, but increase CPU, memory, and machine demand. Grid’s sizing guidance treats capacity as environment-dependent; measure your own workload and increase concurrency gradually instead of assuming a universal sessions-per-machine number.
- Keep setup repeatable: store dependency versions and test commands in project files, and make CI use the same setup path.
- Control parallelism: begin with a small number of sessions. Browser startup, page behavior, memory, and machine limits determine practical throughput.
- Prefer condition-based waits: they reduce unnecessary delays while avoiding races caused by fixed sleeps.
- Budget for browser infrastructure: local runs use developer or CI resources; a self-hosted Grid adds machines and maintenance. The dossier provides no universal cost figure, so estimate from your own browser matrix and concurrency needs.
- Respect target-site rules: browser automation for scraping may be restricted by a site’s terms or blocked by the site. Selenium’s execution guidance calls out this limitation.
10. Or skip the browser setup
If your task is to save a page image or PDF rather than interact with a browser, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. 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}`);
ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup 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 per month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for 1,000 free screenshots a month, with no card required.
11. Frequently asked questions
Can I use Selenium for browser automation outside tests?
Yes. WebDriver can automate browser interactions beyond application tests, but account for the target site’s terms and access rules.
Should I start with Grid?
Usually, start locally with one browser and a test runner. Use Grid when remote browsers, parallel execution, or multiple operating systems and browser versions are real project requirements.
Can I use Selenium to take screenshots?
Yes. A WebDriver session can capture browser screenshots as part of a workflow. If the task is only to retrieve a website screenshot or PDF, ScreenshotNeo offers a direct API call and MCP tools without requiring you to run a browser session.


