Selenium WebDriver automates real browsers. It is the most widely used UI automation tool in the industry, and Selenium with Java is still one of the most requested skills in QA job descriptions.
Setup with Maven
Create a Maven project and add these dependencies to pom.xml (use the latest versions from Maven Central):
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.25.0</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>Since Selenium 4.6, Selenium Manager downloads the right browser driver automatically, so you no longer need to download ChromeDriver by hand.
Your first test
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.*;
public class SearchTest {
WebDriver driver;
@BeforeMethod
public void setUp() {
driver = new ChromeDriver();
driver.manage().window().maximize();
}
@Test
public void pageTitleContainsExample() {
driver.get("https://example.com");
Assert.assertTrue(driver.getTitle().contains("Example"));
}
@AfterMethod
public void tearDown() {
driver.quit();
}
}quit() closes the browser and ends the session. Always call it in @AfterMethod, otherwise browsers pile up on your machine or CI server.
Locators: how Selenium finds elements
| Locator | Example | When to use |
|---|---|---|
id | By.id("email") | Best choice when the id is unique and stable |
name | By.name("password") | Forms |
cssSelector | By.cssSelector("button[type='submit']") | Fast and flexible; preferred over XPath |
xpath | By.xpath("//button[text()='Login']") | When you need text or parent/child navigation |
linkText | By.linkText("Forgot password?") | Links with exact text |
Ask developers to add test attributes likedata-testid="login-button". Then useBy.cssSelector("[data-testid='login-button']"), which does not break when the design changes.
Avoid absolute XPaths such as /html/body/div[2]/div/form/button. One layout change breaks them.
Waits: the key to stable tests
Pages load data asynchronously. If you click before an element is ready, the test fails randomly ("flaky"). Use an explicit wait:
import org.openqa.selenium.support.ui.*;
import java.time.Duration;
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement button = wait.until(ExpectedConditions.elementToBeClickable(By.id("submit")));
button.click();- Do use explicit waits for specific conditions.
- Don't use
Thread.sleep(5000): it always waits the full time and still fails if the page is slower. - Don't mix implicit and explicit waits; it causes unpredictable timeouts.
A realistic login test
@Test
public void validLoginShowsDashboard() {
driver.get("https://your-app.test/login");
driver.findElement(By.id("email")).sendKeys("ravi@test.com");
driver.findElement(By.id("password")).sendKeys("Valid@123");
driver.findElement(By.cssSelector("button[type='submit']")).click();
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.urlContains("/dashboard"));
Assert.assertTrue(driver.findElement(By.tagName("h1")).getText().contains("Welcome"));
}Dropdowns, alerts and frames
import org.openqa.selenium.support.ui.Select;
// <select> dropdown
new Select(driver.findElement(By.id("course"))).selectByVisibleText("Selenium + AI");
// JavaScript alert
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
alert.accept();
// iframe: switch in, act, switch back
driver.switchTo().frame("payment-frame");
driver.findElement(By.id("card-number")).sendKeys("4111111111111111");
driver.switchTo().defaultContent();If an element is inside an iframe, Selenium cannot see it until you switch into that frame. This is a very common reason for NoSuchElementException.
Interview questions
- `findElement` vs `findElements`?
findElementreturns one element or throwsNoSuchElementException;findElementsreturns a list (empty if none). - `close()` vs `quit()`?
close()closes the current window;quit()closes all windows and ends the driver session. - What is the Page Object Model? A design pattern where each page is a class holding its locators and actions, so tests stay readable and locators live in one place.
- How do you handle a StaleElementReferenceException? Find the element again after the page or DOM refreshes, and wait for the new state.
Next steps
Refactor this test using the Page Object Model and run it in parallel with TestNG. Compare it with the modern Playwright approach, or master frameworks in the Selenium + AI course.
