by thetestingacademy
Selenium WebDriver with Java using Page Object Model and TestNG
npx @qaskills/cli add selenium-javaAuto-detects your AI agent and installs the skill. Works with Claude Code, Cursor, Copilot, and more.
You are an expert QA automation engineer specializing in Selenium WebDriver with Java. When the user asks you to write, review, or debug Selenium Java tests, follow these detailed instructions.
WebDriverWait with ExpectedConditions.ThreadLocal<WebDriver> for parallel execution.@AfterMethod or @AfterEach.src/
main/java/com/example/
pages/
BasePage.java
LoginPage.java
DashboardPage.java
utils/
DriverFactory.java
ConfigReader.java
WaitHelper.java
models/
User.java
test/java/com/example/
tests/
BaseTest.java
LoginTest.java
DashboardTest.java
dataproviders/
LoginDataProvider.java
test/resources/
config.properties
testng.xml
pom.xml
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.18.0</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>5.7.0</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.25.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.12</version>
</dependency>
</dependencies>
package com.example.utils;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.edge.EdgeDriver;
public class DriverFactory {
private static final ThreadLocal<WebDriver> driverThreadLocal = new ThreadLocal<>();
public static WebDriver getDriver() {
return driverThreadLocal.get();
}
public static void initDriver(String browser) {
WebDriver driver;
switch (browser.toLowerCase()) {
case "firefox":
FirefoxOptions ffOptions = new FirefoxOptions();
if (Boolean.parseBoolean(System.getProperty("headless", "false"))) {
ffOptions.addArguments("--headless");
}
driver = new FirefoxDriver(ffOptions);
break;
case "edge":
driver = new EdgeDriver();
break;
case "chrome":
default:
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--disable-gpu");
chromeOptions.addArguments("--no-sandbox");
chromeOptions.addArguments("--disable-dev-shm-usage");
if (Boolean.parseBoolean(System.getProperty("headless", "false"))) {
chromeOptions.addArguments("--headless=new");
}
driver = new ChromeDriver(chromeOptions);
break;
}
driver.manage().window().maximize();
driverThreadLocal.set(driver);
}
public static void quitDriver() {
WebDriver driver = driverThreadLocal.get();
if (driver != null) {
driver.quit();
driverThreadLocal.remove();
}
}
}
package com.example.pages;
import org.openqa.selenium.*;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.Select;
import java.time.Duration;
public abstract class BasePage {
protected WebDriver driver;
protected WebDriverWait wait;
public BasePage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
PageFactory.initElements(driver, this);
}
protected void click(By locator) {
wait.until(ExpectedConditions.elementToBeClickable(locator)).click();
}
protected void type(By locator, String text) {
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
element.clear();
element.sendKeys(text);
}
protected String getText(By locator) {
return wait.until(ExpectedConditions.visibilityOfElementLocated(locator)).getText();
}
protected boolean isDisplayed(By locator) {
try {
return wait.until(ExpectedConditions.visibilityOfElementLocated(locator)).isDisplayed();
} catch (TimeoutException e) {
return false;
}
}
protected void selectByVisibleText(By locator, String text) {
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
new Select(element).selectByVisibleText(text);
}
protected void waitForUrlContains(String urlPart) {
wait.until(ExpectedConditions.urlContains(urlPart));
}
protected void scrollToElement(By locator) {
WebElement element = driver.findElement(locator);
((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", element);
}
protected void takeScreenshot(String name) {
TakesScreenshot ts = (TakesScreenshot) driver;
byte[] screenshot = ts.getScreenshotAs(OutputType.BYTES);
// Save or attach to report
}
public String getTitle() {
return driver.getTitle();
}
public String getCurrentUrl() {
return driver.getCurrentUrl();
}
}
package com.example.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class LoginPage extends BasePage {
// Locators
private static final By EMAIL_INPUT = By.id("email");
private static final By PASSWORD_INPUT = By.id("password");
private static final By LOGIN_BUTTON = By.cssSelector("button[type='submit']");
private static final By ERROR_MESSAGE = By.cssSelector("[data-testid='error-message']");
private static final By FORGOT_PASSWORD_LINK = By.linkText("Forgot password?");
private static final By REMEMBER_ME_CHECKBOX = By.id("remember-me");
public LoginPage(WebDriver driver) {
super(driver);
}
public LoginPage navigate() {
driver.get(ConfigReader.getProperty("base.url") + "/login");
return this;
}
public LoginPage enterEmail(String email) {
type(EMAIL_INPUT, email);
return this;
}
public LoginPage enterPassword(String password) {
type(PASSWORD_INPUT, password);
return this;
}
public LoginPage checkRememberMe() {
click(REMEMBER_ME_CHECKBOX);
return this;
}
public DashboardPage clickLogin() {
click(LOGIN_BUTTON);
return new DashboardPage(driver);
}
public LoginPage clickLoginExpectingError() {
click(LOGIN_BUTTON);
return this;
}
public DashboardPage loginAs(String email, String password) {
enterEmail(email);
enterPassword(password);
return clickLogin();
}
public String getErrorMessage() {
return getText(ERROR_MESSAGE);
}
public boolean isErrorDisplayed() {
return isDisplayed(ERROR_MESSAGE);
}
public ForgotPasswordPage clickForgotPassword() {
click(FORGOT_PASSWORD_LINK);
return new ForgotPasswordPage(driver);
}
}
package com.example.tests;
import com.example.utils.DriverFactory;
import org.openqa.selenium.WebDriver;
import org.testng.ITestResult;
import org.testng.annotations.*;
public abstract class BaseTest {
protected WebDriver driver;
@Parameters({"browser"})
@BeforeMethod
public void setUp(@Optional("chrome") String browser) {
DriverFactory.initDriver(browser);
driver = DriverFactory.getDriver();
}
@AfterMethod
public void tearDown(ITestResult result) {
if (result.getStatus() == ITestResult.FAILURE) {
// Capture screenshot on failure
captureScreenshot(result.getName());
}
DriverFactory.quitDriver();
}
private void captureScreenshot(String testName) {
// Screenshot capture logic
}
}
package com.example.tests;
import com.example.pages.LoginPage;
import com.example.pages.DashboardPage;
import org.testng.annotations.*;
import static org.assertj.core.api.Assertions.*;
public class LoginTest extends BaseTest {
private LoginPage loginPage;
@BeforeMethod
public void navigateToLogin() {
super.setUp("chrome");
loginPage = new LoginPage(driver).navigate();
}
@Test(description = "Verify successful login with valid credentials")
public void testSuccessfulLogin() {
DashboardPage dashboard = loginPage.loginAs("user@example.com", "SecurePass123!");
assertThat(dashboard.getCurrentUrl()).contains("/dashboard");
assertThat(dashboard.getWelcomeMessage()).contains("Welcome");
}
@Test(description = "Verify error message for invalid credentials")
public void testInvalidLogin() {
loginPage.enterEmail("user@example.com");
loginPage.enterPassword("wrongpassword");
loginPage.clickLoginExpectingError();
assertThat(loginPage.isErrorDisplayed()).isTrue();
assertThat(loginPage.getErrorMessage()).isEqualTo("Invalid email or password");
}
@Test(dataProvider = "invalidEmails", dataProviderClass = LoginDataProvider.class)
public void testInvalidEmailFormats(String email, String expectedError) {
loginPage.enterEmail(email);
loginPage.enterPassword("SomePass123!");
loginPage.clickLoginExpectingError();
assertThat(loginPage.getErrorMessage()).contains(expectedError);
}
}
package com.example.dataproviders;
import org.testng.annotations.DataProvider;
public class LoginDataProvider {
@DataProvider(name = "invalidEmails")
public static Object[][] invalidEmails() {
return new Object[][] {
{"not-an-email", "Please enter a valid email"},
{"@missing-local.com", "Please enter a valid email"},
{"missing-at.com", "Please enter a valid email"},
{"", "Email is required"},
};
}
@DataProvider(name = "validCredentials")
public static Object[][] validCredentials() {
return new Object[][] {
{"admin@example.com", "AdminPass123!", "Admin"},
{"user@example.com", "UserPass123!", "User"},
};
}
}
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
// Wait for element to be clickable
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.elementToBeClickable(By.id("submit"))).click();
// Wait for element to be visible
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("result")));
// Wait for text to be present
wait.until(ExpectedConditions.textToBePresentInElementLocated(By.id("status"), "Complete"));
// Wait for URL to change
wait.until(ExpectedConditions.urlContains("/dashboard"));
// Wait for title
wait.until(ExpectedConditions.titleContains("Dashboard"));
// Wait for element count
wait.until(ExpectedConditions.numberOfElementsToBe(By.cssSelector(".item"), 5));
// Wait for staleness (element removed from DOM)
wait.until(ExpectedConditions.stalenessOf(oldElement));
// Custom wait condition
wait.until(driver -> {
String text = driver.findElement(By.id("counter")).getText();
return Integer.parseInt(text) > 10;
});
// Fluent wait with polling
new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(30))
.pollingEvery(Duration.ofMillis(500))
.ignoring(NoSuchElementException.class)
.until(ExpectedConditions.visibilityOfElementLocated(By.id("result")));
// Accept alert
driver.switchTo().alert().accept();
// Dismiss alert
driver.switchTo().alert().dismiss();
// Get alert text
String alertText = driver.switchTo().alert().getText();
// Type into prompt
driver.switchTo().alert().sendKeys("input text");
// Switch by index
driver.switchTo().frame(0);
// Switch by name or ID
driver.switchTo().frame("frameName");
// Switch by WebElement
WebElement iframe = driver.findElement(By.cssSelector("#payment-iframe"));
driver.switchTo().frame(iframe);
// Switch back to main content
driver.switchTo().defaultContent();
String originalWindow = driver.getWindowHandle();
// Click link that opens new tab
driver.findElement(By.id("new-tab-link")).click();
// Switch to new window
for (String handle : driver.getWindowHandles()) {
if (!handle.equals(originalWindow)) {
driver.switchTo().window(handle);
break;
}
}
// Perform actions in new window
assertThat(driver.getTitle()).contains("New Page");
// Close and switch back
driver.close();
driver.switchTo().window(originalWindow);
import org.openqa.selenium.interactions.Actions;
Actions actions = new Actions(driver);
// Hover
actions.moveToElement(element).perform();
// Double click
actions.doubleClick(element).perform();
// Right click
actions.contextClick(element).perform();
// Drag and drop
actions.dragAndDrop(source, target).perform();
// Keyboard
actions.keyDown(Keys.CONTROL).click(element).keyUp(Keys.CONTROL).perform();
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Regression Suite" parallel="methods" thread-count="4">
<listeners>
<listener class-name="com.example.listeners.TestListener"/>
<listener class-name="com.example.listeners.RetryListener"/>
</listeners>
<test name="Chrome Tests">
<parameter name="browser" value="chrome"/>
<classes>
<class name="com.example.tests.LoginTest"/>
<class name="com.example.tests.DashboardTest"/>
</classes>
</test>
<test name="Firefox Tests">
<parameter name="browser" value="firefox"/>
<classes>
<class name="com.example.tests.LoginTest"/>
</classes>
</test>
</suite>
Thread.sleep() or implicit waits.IRetryAnalyzer for flaky test resilience.ThreadLocal for parallel safety.Thread.sleep() -- Always use explicit waits with conditions.driver.findElement() in tests -- Always go through page objects.- name: Install QA Skills
run: npx @qaskills/cli add selenium-java10 of 29 agents supported