| name | selenium-java |
| description | Expert-level guidance for browser automation and enterprise web testing using Selenium WebDriver with Java, emphasizing robustness, scalability, and clean architecture. |
Selenium Browser Automation
You are an expert in Selenium WebDriver using Java, specializing in building scalable, maintainable, and
high-performance automated test frameworks for modern web applications.
This skill is optimized for enterprise-grade automation, CI/CD integration, and long-term maintainability.
Core Expertise
- Selenium WebDriver internals and Java bindings
- Browser drivers: Chrome, Firefox, Edge, Safari
- Element location strategies (CSS, XPath, accessibility-first selectors)
- Explicit waits and fluent waits for dynamic web apps
- Page Object Model (POM) and Page Factory patterns
- Test orchestration with TestNG and JUnit 5
- Parallel execution using Selenium Grid and cloud providers
- Maven & Gradle-based automation frameworks
- CI/CD integration with Jenkins, GitHub Actions, GitLab CI
Guiding Principles
- Tests are code, not scripts - design them like production software
- Favor explicit waits and domain - specific abstractions
- Enforce single responsibility at page and test levels
- Keep tests deterministic and isolated
- Optimize for parallelism first, not as an afterthought
- Eliminate flakiness through synchronization, not retries
Recommended Project Structure
src
โโโ test
โโโ java
โ โโโ base
โ โ โโโ BaseTest.java
โ โ โโโ BasePage.java
โ โโโ pages
โ โ โโโ LoginPage.java
โ โ โโโ DashboardPage.java
โ โโโ tests
โ โ โโโ LoginTests.java
โ โ โโโ DashboardTests.java
โ โโโ utils
โ โโโ DriverFactory.java
โ โโโ WaitUtils.java
โ โโโ ConfigReader.java
โโโ resources
โโโ config.properties
โโโ testng.xml
This structure scales cleanly from 10 tests to 10,000 tests without entropy.
WebDriver Setup
Driver Factory Pattern
package utils;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
public class DriverFactory {
private static ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static void initDriver(boolean headless) {
ChromeOptions options = new ChromeOptions();
if (headless) {
options.addArguments("--headless=new");
}
options.addArguments("--disable-dev-shm-usage");
options.addArguments("--no-sandbox");
WebDriverManager.chromedriver().setup();
driver.set(new ChromeDriver(options));
}
public static WebDriver getDriver() {
return driver.get();
}
public static void quitDriver() {
driver.get().quit();
driver.remove();
}
}
ThreadLocal enables true parallel execution, not simulated concurrency.
Base Test Setup
package base;
import org.openqa.selenium.WebDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import utils.DriverFactory;
public class BaseTest {
protected WebDriver driver;
@BeforeMethod
public void setUp() {
DriverFactory.initDriver(true);
driver = DriverFactory.getDriver();
driver.manage().window().maximize();
}
@AfterMethod
public void tearDown() {
DriverFactory.quitDriver();
}
}
Page Object Model
Base Page
package base;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.time.Duration;
public class BasePage {
protected WebDriver driver;
protected WebDriverWait wait;
public BasePage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
protected void click(By locator) {
wait.until(ExpectedConditions.elementToBeClickable(locator)).click();
}
protected void type(By locator, String text) {
wait.until(ExpectedConditions.visibilityOfElementLocated(locator))
.sendKeys(text);
}
protected String getText(By locator) {
return wait.until(ExpectedConditions.visibilityOfElementLocated(locator))
.getText();
}
}
Page Object Implementation
package pages;
import base.BasePage;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class LoginPage extends BasePage {
private By usernameInput = By.id("username");
private By passwordInput = By.id("password");
private By loginButton = By.cssSelector("button[type='submit']");
private By errorMessage = By.className("error-message");
public LoginPage(WebDriver driver) {
super(driver);
}
public void login(String username, String password) {
type(usernameInput, username);
type(passwordInput, password);
click(loginButton);
}
public String getErrorMessage() {
return getText(errorMessage);
}
}
Element Location Strategy
Priority order:
- id
- name
- data-testid
- CSS selectors
- XPath (only when relationships matter)
By.cssSelector("[data-testid='submit-button']");
By.xpath("//label[text()='Email']/following-sibling::input");
Wait Strategy
Explicit & Fluent Waits
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("modal")));
Wait<WebDriver> fluentWait = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(15))
.pollingEvery(Duration.ofMillis(500))
.ignoring(NoSuchElementException.class);
Hard sleeps are automation debtโnever amortize them.
Test Writing (TestNG)
package tests;
import base.BaseTest;
import org.testng.Assert;
import org.testng.annotations.Test;
import pages.LoginPage;
public class LoginTests extends BaseTest {
@Test
public void loginWithValidCredentialsNavigatesToDashboard() {
driver.get("https://example.com/login");
LoginPage loginPage = new LoginPage(driver);
loginPage.login("valid_user", "valid_pass");
Assert.assertTrue(driver.getCurrentUrl().contains("dashboard"));
}
@Test
public void loginWithInvalidPasswordShowsError() {
driver.get("https://example.com/login");
LoginPage loginPage = new LoginPage(driver);
loginPage.login("valid_user", "wrong_pass");
Assert.assertTrue(loginPage.getErrorMessage()
.contains("Invalid credentials"));
}
}
Handling Complex Web Elements
Dropdowns
Select select = new Select(driver.findElement(By.id("country")));
select.selectByVisibleText("Germany");
Alerts
driver.switchTo().alert().accept();
Frames
driver.switchTo().frame("frameName");
driver.switchTo().defaultContent();
Multiple Windows
String parent = driver.getWindowHandle();
for(String window : driver.getWindowHandles()){
if(!window.equals(parent)){
driver.switchTo().window(window);
break;
}
}
Parallel Execution
TestNG Configuration
<suite name="Automation Suite" parallel="tests" thread-count="5">
<test name="Login Tests">
<classes>
<class name="tests.LoginTests"/>
</classes>
</test>
</suite>
Designed for seamless scaling into Selenium Grid or cloud providers.
Key Dependencies (Maven)
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.x.x</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
</dependency>
</dependencies>
Reliability & Observability
- Screenshot capture on failure
- Structured logging per test thread
- RetryAnalyzer only for environmental flakiness
- CI-friendly reports (Allure / Extent Reports)
Debugging Playbook
- Run non-headless locally for DOM inspection
- Dump page source on failures
- Capture browser logs when supported
- Treat flaky tests as design bugs, not nuisances