The Spring Boot of Selenium — Playwright-inspired APIs, zero setup, and enterprise features, without hiding Selenium
Documentation · Starter Template · Sample Project · Changelog
The same login flow against a real page, written both ways. Both tests pass.
Three files. Copy them as-is and mvn test goes green against a real Chrome.
Prerequisites: Java 17+, Maven 3.8+, Chrome installed. No WebDriver binaries — Selenium Manager fetches them.
1. pom.xml
<properties>
<maven.compiler.release>17</maven.compiler.release>
</properties>
<dependencies>
<dependency>
<groupId>io.github.seleniumboot</groupId>
<artifactId>selenium-boot</artifactId>
<version>3.8.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
</plugin>
</plugins>
</build>2. selenium-boot.yml — project root, next to pom.xml. The test below calls open(), which needs baseUrl. The browser, mode and timeouts shown are the built-in defaults, listed for reference.
execution:
mode: local
baseUrl: https://example.com
browser:
name: chrome
timeouts:
explicit: 10
pageLoad: 303. src/test/java/SmokeTest.java
import com.seleniumboot.locator.Role;
import com.seleniumboot.test.BaseTest;
import org.testng.annotations.Test;
public class SmokeTest extends BaseTest {
@Test
public void opensThePage() {
open();
assertThat(getByRole(Role.HEADING, "Example Domain")).isVisible();
assertThat(getByRole(Role.LINK)).isVisible();
}
}Then:
mvn testNo driver setup, no teardown, no waits, no WebDriver to manage — BaseTest owns the lifecycle. The HTML report lands at target/selenium-boot-report.html.
The report from the sample project's suite (102 tests, parallel).
Note what the test doesn't contain: no CSS selector, no XPath, no WebDriverWait. getByRole
finds elements the way a screen reader does, and assertThat(...).isVisible() retries until the
timeout instead of failing on the first miss. Both are available on every test and page object.
Illustrative — a sign-in flow, raw Selenium on the left of the line, Selenium Boot below it.
// Plain Selenium + TestNG
WebDriver driver = new ChromeDriver();
driver.get("https://app.example.com");
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("#email")))
.sendKeys("ada@example.com");
driver.findElement(By.cssSelector("#password")).sendKeys("hunter2");
driver.findElement(By.cssSelector("button.btn-primary[type='submit']")).click();
wait.until(ExpectedConditions.visibilityOfElementLocated(
By.xpath("//*[contains(text(),'Welcome back')]")));
driver.quit();// Selenium Boot — same flow
open();
getByLabel("Email").type("ada@example.com");
getByLabel("Password").type("hunter2");
getByRole(Role.BUTTON, "Sign in").click();
assertThat(getByText("Welcome back")).isVisible();The second version has no driver lifecycle, no explicit waits, and nothing coupled to the
markup — rename a CSS class or reorder the DOM and it still passes. The raw WebDriver is
still there via getDriver() whenever you need it.
Next: page objects and tests, configuration, and the documentation.
Selenium Boot is an opinionated framework for Java Selenium, inspired by Spring Boot. Add one dependency, extend BaseTest / BasePage, and the driver lifecycle, waits, retries, reporting and CI wiring are already decided.
- Convention over configuration. Browser, mode and timeouts have built-in defaults;
selenium-boot.ymlonly says what you want to change. - Never hides Selenium. The raw
WebDriver/By/WebElementis always one call away viagetDriver(). - Extensible, not required. Custom drivers, report adapters and hooks plug in through SPI. Most users never touch it.
Already on Selenium? You keep your stack, TestNG, team skills and Grid, and gain accessibility-first locators, auto-waiting and web-first assertions. Why it exists and how it compares: Why Selenium Boot.
Outcomes first — the API that delivers each one is named so you can find it in the docs.
- No driver setup, teardown or
Thread.sleep()— thread-safe WebDriver lifecycle per test and an auto-waitingWaitEngine - Tests less coupled to markup — accessibility-first locators (
getByRole,getByText,getByLabel,getByPlaceholder,getByTestId,getByAltText,getByTitle) plus aSmartLocatorfallback - Transient failures don't fail the build — automatic retry via
@Retryable; flaky tests are still reported as retried - Parallel runs that are safe — thread-isolated drivers,
parallelin one YAML line - See exactly why a test failed — screenshot on failure,
StepLoggernamed steps, and an HTML report with pass-rate gauge, slowest tests and step timeline - Write pages, not plumbing —
BasePagewith wait-backedclick,type,getText, iFrame helpers and file upload;@PreConditionlogs in once and reuses the session - UI and API in the same suite —
BaseApiTest+ fluentApiClientwith auth, schema validation and JSONPath - CI-aware defaults — detects GitHub Actions, Jenkins, CircleCI and others; forces headless, emits JUnit XML
Also built in: accessibility checks (axe-core bundled), download testing, JavaScript console-error capture, environment profiles, and SPI plugins for custom drivers, report adapters and hooks.
Extend BasePage for the same semantic locators plus wait-backed helpers (click, type, getText, isDisplayed, withinFrame, upload):
public class LoginPage extends BasePage {
public LoginPage(WebDriver driver) {
super(driver);
}
public void login(String username, String password) {
getByLabel("Username").type(username);
getByLabel("Password").type(password);
getByRole(Role.BUTTON, "Log in").click();
}
}Prefer semantic locators. When an element has no accessible name, fall back to By — type(By.id("username"), username) — or to the raw WebDriver.
Extend BaseTest — that's all the setup a test needs:
public class LoginTest extends BaseTest {
@Test
public void loginWithValidCredentials() {
StepLogger.step("Open login page");
open();
StepLogger.step("Enter credentials and submit", true);
new LoginPage(getDriver()).login("admin", "password123");
assertTrue(getDriver().getCurrentUrl().contains("/dashboard"));
}
}- Never instantiate or quit
WebDriveryourself — the framework manages it. getDriver()returns the current thread's driver.open()navigates tobaseUrl;open("/path")to a sub-path.
selenium-boot.yml lives at the project root, next to pom.xml. It is optional: omit the file, or any key, and the built-in default applies. The one exception is execution.baseUrl, which has no default and is needed by open(). Values that are present but invalid (an unknown execution.mode, a negative timeout) fail at startup.
execution:
mode: local # local | remote
baseUrl: https://example.com
parallel: methods # none | methods | classes
threadCount: 4
browser:
name: chrome # chrome | firefox
headless: false
retry:
enabled: true
maxAttempts: 2
timeouts:
explicit: 10 # seconds — used by WaitEngine
pageLoad: 30 # secondsRun on a Selenium Grid by switching mode — no code changes:
execution:
mode: remote
gridUrl: http://localhost:4444/wd/hubEnvironment profiles (-Dselenium.boot.profile=staging), ci: quality gates and every other key are in the configuration reference; the api: keys are in API testing.
| Core | BaseTest · BasePage · Locators · Waits · Retry · Parallel · @PreCondition |
| API testing | API testing · Authentication · Scenario & suite context |
| Reporting | HTML report · JUnit XML |
| CI/CD | GitHub Actions · Jenkins · Quality gates |
| Extending | Custom drivers · Report adapters · Hooks · Plugins |
| Migrating | From Selenium + TestNG · Coming from Playwright |
| Examples | Sample project · Starter template |
selenium-boot-migrator analyzes it first and
changes nothing: what maps cleanly, what needs manual work. Maven and Gradle (including version catalogs).
java -jar selenium-boot-migrator.jar analyze path/to/your-projectGrab the jar from the latest release. Requires Java 17+.
Let an AI assistant drive a real browser and write the tests seleniumboot-mcp is a standalone MCP server for Claude / GitHub Copilot: it controls Chrome, records your session, and generates ready-to-run test code (Java TestNG / JUnit 5 / Gherkin, Python, C#, Playwright) — Selenium Boot-native when the dependency is present. Its
migratetool runs the analysis above for you.pip install seleniumboot-mcpPyPI · GitHub · 43 tools by default (77 total) · self-healing locators
Current release: v3.8.0 — Locator.first() / Locator.last() and a results-first HTML report (suite wall-clock time in hh:mm:ss).
See the full version history in CHANGELOG.md.
Licensed under the Apache License, Version 2.0.
Contributions are warmly welcome — Selenium Boot is opinionated, and contributions that align with its philosophy (zero boilerplate, convention over configuration, never hide Selenium) help the whole community.
New here? The best place to start:
- 🙌 Good first issues — scoped, self-contained tasks
- 🤝 Help wanted — larger pieces we'd love a hand with
- 🗺️ Roadmap — where the project is heading and where help fits
- 💬 Discussions — questions and feature ideas
Then read CONTRIBUTING.md for dev setup, the PR checklist, and the backward-compatibility policy. Bug reports and feature requests both have issue templates to guide you.
Thanks to everyone who has contributed to Selenium Boot. Your bug reports, ideas, documentation, and code help make the project better. Meet the contributors.
Selenium Boot is an independent open-source project and is not affiliated with Selenium or the Spring Framework.

