Automation for Java developers · Selenium

Selenium with Java: From Zero to Real Tests

You already know Java. This guide teaches only what is new: Selenium words, objects and methods, and how to use them to test a real web page. Every code example is Java and was run successfully.

Important

Red boxes and rows: learn these first. They are used in almost every test and asked in almost every interview.

Good to know

Blue boxes and rows: useful, but you can learn them later or only when needed.

1. What is Selenium and how does it work?

Selenium is a Java library (a set of classes in a jar file) that controls a real browser. Your Java code says "open this page", "type here", "click that button", and the browser does it. Then your code checks the result.

Your Java test→Selenium library→Browser driver (chromedriver)→Chrome browser

Selenium cannot talk to Chrome directly. A small program called the browser driver sits in the middle and translates the commands. In Selenium 4, a helper called Selenium Manager downloads the correct driver for you automatically.

Words you will hear

WordSimple meaning
WebDriverThe main Java interface. One WebDriver object = one browser you can control.
Driver / ChromeDriverWebDriver is the interface. ChromeDriver is the class that opens Chrome. FirefoxDriver and EdgeDriver do the same for other browsers.
WebElementOne thing on the page: a button, text box, link, label. Your code holds it in a WebElement variable.
Locator (By)The way to tell Selenium which element you mean: by id, name, CSS, XPath and so on.
SessionFrom new ChromeDriver() to driver.quit(). One browser window lifetime.
DOMThe page as a tree of elements. Locators search this tree.
HeadlessRunning the browser with no visible window. Faster, used on servers.
Selenium GridRuns tests on many machines and browsers at once. Good to know only.
Selenium IDEA browser plugin that records clicks. Not used in real projects.

2. Setup in Java (Maven)

ImportantDependencies

Create a Maven project, then add these dependencies to pom.xml. Maven downloads the jars for you.

<dependency>
  <groupId>org.seleniumhq.selenium</groupId>
  <artifactId>selenium-java</artifactId>
  <version>4.27.0</version>
</dependency>
<dependency>
  <groupId>org.testng</groupId>
  <artifactId>testng</artifactId>
  <version>7.10.2</version>
</dependency>

What it does: selenium-java is Selenium itself. testng is the test framework that runs your tests and gives you Assert. Use a recent Selenium version if you see a newer one on Maven Central. A "CDP version" warning in the console is harmless.

Also install Google Chrome. Nothing else is needed, Selenium Manager gets the driver.

Your first test (complete)

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class FirstTest {
    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();                 // 1. open Chrome
        driver.get("https://example.com");                      // 2. go to a page
        String title = driver.getTitle();                       // 3. read the page title
        System.out.println(title);
        driver.findElement(By.tagName("a")).click();            // 4. find a link and click it
        driver.quit();                                          // 5. close the browser
    }
}

What it does: Five steps that every Selenium test has: open the browser, go to a page, find elements and act on them, check something, close the browser.

ImportantWhy WebDriver on the left side?

WebDriver driver = new ChromeDriver(); The variable type is the interface WebDriver, the object is ChromeDriver. This is Java polymorphism. To run on Firefox you only change the right side to new FirefoxDriver(). Everything else stays the same.

ImportantNever forget quit()

Always call driver.quit(). If you forget it, browser windows stay open and eat memory. In a test class, put it in @AfterMethod so it runs even when the test fails.

3. All the main classes in one picture

Class / interfaceUse
WebDriverControls the browser: open pages, find elements, switch windows.
WebElementOne element: click it, type in it, read its text.
ByDescribes how to find an element (id, CSS, XPath...).
SelectHelper for <select> dropdowns.
ActionsMouse and keyboard actions: hover, double click, drag and drop.
AlertBrowser popups (alert, confirm, prompt).
WebDriverWait + ExpectedConditionsWait until something is true.
JavascriptExecutorRun JavaScript inside the page.
KeysSpecial keys: ENTER, TAB, BACK_SPACE...
ChromeOptionsSettings for Chrome: headless, window size, notifications off.

4. WebDriver methods (the browser)

These are called on the driver object. Red rows you will use daily.

MethodWhat it doesExample
Importantget(String url)Open a web page and wait until it loads.driver.get("https://shop.com");
ImportantgetTitle()Returns the page title.String t = driver.getTitle();
ImportantgetCurrentUrl()Returns the current address.driver.getCurrentUrl();
ImportantfindElement(By)Finds the first matching element. Throws NoSuchElementException if none.driver.findElement(By.id("email"));
ImportantfindElements(By)Finds all matching elements as a List. Empty list if none (no error).List<WebElement> rows = driver.findElements(By.tagName("tr"));
Importantquit()Closes every window and ends the session.driver.quit();
Importantclose()Closes only the current window or tab.driver.close();
Importantnavigate().back() / forward() / refresh() / to(url)Browser back, forward, reload, go to a URL.driver.navigate().back();
Importantmanage().window().maximize()Makes the window full size.driver.manage().window().maximize();
Importantmanage().timeouts().implicitlyWait(Duration)Global wait for finding elements (see waits section).driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
ImportantswitchTo().frame(...)Go inside an iframe. Use name, id, index or WebElement.driver.switchTo().frame("payFrame");
ImportantswitchTo().defaultContent()Come back from a frame to the main page.driver.switchTo().defaultContent();
ImportantswitchTo().window(String handle)Move to another window or tab.driver.switchTo().window(handle);
ImportantswitchTo().alert()Take control of a browser popup.Alert a = driver.switchTo().alert();
ImportantgetWindowHandle() / getWindowHandles()Id of the current window / ids of all open windows (a Set<String>).Set<String> all = driver.getWindowHandles();
Good to knowgetPageSource()The full HTML of the page as a String.driver.getPageSource();
Good to knowswitchTo().newWindow(WindowType.TAB)Open a new tab and move to it.driver.switchTo().newWindow(WindowType.TAB);
Good to knowswitchTo().parentFrame()Go up one frame level.driver.switchTo().parentFrame();
Good to knowmanage().window().minimize() / fullscreen() / setSize()Other window controls.driver.manage().window().setSize(new Dimension(1280, 800));
Good to knowmanage().timeouts().pageLoadTimeout(Duration)How long to wait for a page to load before failing.
Good to knowmanage().getCookies() / addCookie() / deleteAllCookies()Read, add and clear cookies. deleteAllCookies() gives a fresh start.driver.manage().deleteAllCookies();

Importantclose() vs quit()

close() closes one window. quit() closes everything. Interviewers ask this difference very often.

ImportantfindElement vs findElements

findElement gives one element and fails if none is found. findElements gives a list and never fails. Use findElements(...).size() == 0 to check that something is NOT on the page.

5. Locators: how to find an element

A web page is HTML. Each element has attributes like id, name, class. To click a button, you first find it with a locator from the By class.

<input id="email" name="email" type="email" class="form-control" placeholder="you@example.com">
<button type="submit" class="btn">Login</button>

What it does: Example HTML we will find elements in.

MethodWhat it doesExample
ImportantBy.id("email")Finds by the id attribute. Fastest and best when the id is unique and stable.driver.findElement(By.id("email"))
ImportantBy.cssSelector("input#email")Finds with a CSS selector. Fast and flexible.By.cssSelector("button[type='submit']")
ImportantBy.xpath("//button[text()='Login']")Finds with XPath. Most powerful: can use text and parent / child.By.xpath("//input[@id='email']")
ImportantBy.name("email")Finds by the name attribute.By.name("email")
ImportantBy.linkText("Forgot password?")Finds a link by its full visible text.By.linkText("Sign up")
Good to knowBy.partialLinkText("Forgot")Link text that contains the given part.
Good to knowBy.className("btn")Finds by one class name. Many elements often share a class.By.className("btn")
Good to knowBy.tagName("a")Finds by tag. Used to get lists like all links or all rows.driver.findElements(By.tagName("a"))
Good to knowRelativeLocator.with(By.tagName("input")).below(By.id("email"))Selenium 4: find by position (above, below, toLeftOf, toRightOf, near).

CSS and XPath cheat sheet

Want to findCSS selectorXPath
By id#email//*[@id='email']
By class.btn//*[@class='btn']
Tag with attributeinput[type='email']//input[@type='email']
By exact textnot possible//button[text()='Login']
By part of textnot possible//button[contains(text(),'Log')]
Part of an attributediv[class*='err']//div[contains(@class,'err')]
Starts withinput[id^='user']//input[starts-with(@id,'user')]
Child inside parent#product-3 button//div[@id='product-3']//button
Second item of manyli:nth-child(2)(//li)[2]
Next to a labelnot possible//label[text()='Email']/following::input[1]
Two conditionsinput[type='radio'][value='UPI']//input[@type='radio' and @value='UPI']

ImportantLocator rules

6. WebElement methods (the things on the page)

MethodWhat it doesExample
Importantclick()Clicks the element.driver.findElement(By.id("loginBtn")).click();
ImportantsendKeys(CharSequence...)Types text. Also sends special keys. On a file input, sends the file path.box.sendKeys("hello");
Importantclear()Removes the text of an input.box.clear();
ImportantgetText()Returns the visible text of the element.String msg = el.getText();
ImportantgetAttribute("name")Returns an attribute or property such as value, href, class, type. To read what a user typed, use getAttribute("value").box.getAttribute("value");
ImportantisDisplayed()True if the element is visible on the page.el.isDisplayed();
ImportantisEnabled()True if the element is not disabled. Use for buttons.btn.isEnabled();
ImportantisSelected()True if a checkbox, radio button or option is selected.box.isSelected();
Good to knowgetDomAttribute("name")Selenium 4: the attribute exactly as written in the HTML.el.getDomAttribute("data-state");
Good to knowgetDomProperty("name")Selenium 4: the live property value of the element.el.getDomProperty("value");
Good to knowgetTagName()The tag, such as input.
Good to knowgetCssValue("color")A style value. Used for colour or font checks.el.getCssValue("color");
Good to knowgetSize() / getLocation() / getRect()Width, height and position on the page.
Good to knowsubmit()Submits the form that contains the element.
Good to knowfindElement(By) / findElements(By)Search only inside this element (a child search).card.findElement(By.tagName("button"));
Good to knowgetScreenshotAs(OutputType.FILE)Screenshot of just this element.

ImportantCommon mix-ups

7. Special elements

Dropdown: the Select class

Works only for the HTML <select> tag. Wrap the element in Select.

Select country = new Select(driver.findElement(By.id("country")));
country.selectByVisibleText("India");     // choose by the text the user sees
country.selectByValue("USA");             // choose by the value attribute
country.selectByIndex(0);                 // choose by position, starts at 0
String chosen = country.getFirstSelectedOption().getText();

What it does: Selects an option in three ways and reads the chosen one.

MethodWhat it doesExample
ImportantselectByVisibleText(String)Choose by the visible text.
ImportantselectByValue(String)Choose by the value attribute.
ImportantgetOptions()List of all options as List<WebElement>.select.getOptions().size();
ImportantgetFirstSelectedOption()The selected option (single dropdown).
Good to knowselectByIndex(int)Choose by position.
Good to knowisMultiple()True for a multi-select list.
Good to knowgetAllSelectedOptions()All selected options of a multi-select.
Good to knowdeselectAll() / deselectByValue() / deselectByVisibleText()Only for multi-select lists.

Popups: the Alert interface

Alert alert = wait.until(ExpectedConditions.alertIsPresent());   // wait for the popup
String text = alert.getText();     // read the message
alert.accept();                    // press OK
// alert.dismiss();                // press Cancel
// alert.sendKeys("abc");          // type into a prompt box

What it does: Waits for a popup, reads it, and presses OK.

ImportantAlert rule

While a popup is open, Selenium cannot do anything else on the page. You will get UnhandledAlertException. Always handle the popup first.

Mouse and keyboard: the Actions class

Actions actions = new Actions(driver);
actions.moveToElement(menu).perform();                 // hover
actions.doubleClick(button).perform();                 // double click
actions.contextClick(item).perform();                  // right click
actions.dragAndDrop(source, target).perform();         // drag and drop
actions.sendKeys(Keys.ESCAPE).perform();               // press a key

What it does: Every Actions chain must end with perform(), or nothing happens.

Special keys

box.sendKeys("selenium", Keys.ENTER);                   // type and press Enter
box.sendKeys(Keys.BACK_SPACE);                          // delete one character
box.sendKeys(Keys.chord(Keys.CONTROL, "a"));            // Ctrl + A (use Keys.COMMAND on Mac)

What it does: Keys has ENTER, TAB, ESCAPE, BACK_SPACE, ARROW_UP, ARROW_DOWN and more.

JavascriptExecutor

JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("arguments[0].scrollIntoView(true);", element);      // scroll to element
js.executeScript("arguments[0].click();", element);                   // click with JavaScript
js.executeScript("arguments[0].value = arguments[1];", dateBox, "2000-05-20");   // set a value
Object title = js.executeScript("return document.title;");

What it does: Runs JavaScript inside the page. arguments[0] is the first value you pass after the script text.

Use it when normal Selenium cannot do the job: date pickers, hidden elements, scrolling, or a click that is blocked by another element. Try normal Selenium first.

Frames, windows and file upload

driver.switchTo().frame("payFrame");           // go inside an iframe
driver.switchTo().defaultContent();             // come back to the main page

String main = driver.getWindowHandle();          // remember the main window id
for (String h : driver.getWindowHandles()) {
    if (!h.equals(main)) driver.switchTo().window(h);   // move to the new tab
}

driver.findElement(By.id("resume")).sendKeys("/Users/me/resume.pdf");   // file upload

What it does: For an <input type="file"> you do NOT click. You send the full path of the file as text.

Screenshot

File shot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Files.copy(shot.toPath(), Path.of("failure.png"));

What it does: Saves a picture of the page. Normally done when a test fails.

8. Waits: the most important topic

ImportantWhy we need waits

A page needs time to load. If your code looks for a button before it appears, you get NoSuchElementException. A wait tells Selenium to give the page a little time. Without waits, tests fail randomly.

WaitHow it worksAdvice
ImplicitOne global rule: for every findElement, keep trying up to N seconds.Simple, but do not mix with explicit waits.
Explicit (WebDriverWait)Wait for ONE condition on ONE element (visible, clickable...).Best choice. Use this.
FluentExplicit wait where you also set how often to check and which errors to ignore.For special cases.
Thread.sleepStops for a fixed time.Avoid. It always waits the full time.
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));

What it does: Implicit wait: for the whole test, find calls retry for up to 5 seconds.

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement msg = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("msg")));
wait.until(ExpectedConditions.elementToBeClickable(By.id("payBtn"))).click();

What it does: Waits up to 10 seconds, but continues the moment the condition is true. If it never becomes true, a TimeoutException is thrown.

MethodWhat it doesExample
ImportantvisibilityOfElementLocated(By)Element is in the page and visible. Returns the element.
ImportantelementToBeClickable(By)Element is visible and enabled, ready to click.
ImportantpresenceOfElementLocated(By)Element exists in the HTML (it may still be hidden).
ImportantalertIsPresent()A popup is open.
Good to knowinvisibilityOfElementLocated(By)Element has gone (for example a loading spinner).
Good to knowtextToBePresentInElementLocated(By, text)Element contains the given text.
Good to knowtitleContains(text) / urlContains(text)Page title or URL has the text.
Good to knowframeToBeAvailableAndSwitchToIt(...)Frame is ready and Selenium switches into it.
Good to knownumberOfElementsToBe(By, n)Exactly n elements found.
Good to knowstalenessOf(element)The old element is gone from the page.
Wait<WebDriver> fluent = new FluentWait<>(driver)
        .withTimeout(Duration.ofSeconds(20))
        .pollingEvery(Duration.ofMillis(500))
        .ignoring(NoSuchElementException.class);
WebElement el = fluent.until(d -> d.findElement(By.id("late")));

What it does: Fluent wait: check every 500 ms for up to 20 seconds, and ignore "not found" errors while waiting.

9. Common exceptions

ExceptionWhy it happensFix
ImportantNoSuchElementExceptionElement not found. Wrong locator, page not loaded, or inside a frame.Fix the locator, add a wait, switch to the frame.
ImportantTimeoutExceptionAn explicit wait never became true.Check condition and locator.
ImportantStaleElementReferenceExceptionThe page refreshed and your saved element is old.Find the element again.
ImportantElementClickInterceptedExceptionAnother element (banner, popup) covers the button.Close the cover, scroll, or wait.
Good to knowElementNotInteractableExceptionElement exists but is hidden or disabled.Wait until visible or enabled.
Good to knowNoAlertPresentExceptionYou switched to an alert but none is open.Wait for alertIsPresent().
Good to knowUnhandledAlertExceptionA popup is open and blocks the next action.Accept or dismiss it first.
Good to knowSessionNotCreatedExceptionBrowser and driver versions do not match.Update Selenium or Chrome.

10. TestNG: running and checking tests

Selenium only controls the browser. TestNG runs your methods as tests and tells you pass or fail. It works like JUnit with extra features.

MethodWhat it doesExample
Important@TestMarks a method as a test.
Important@BeforeMethod / @AfterMethodRun before and after EACH test. Use for opening and closing the browser.
Important@DataProviderGives many rows of data to one test method (data-driven testing).@Test(dataProvider = "logins")
Good to know@BeforeClass / @AfterClassRun once for the whole class.
Good to know@BeforeSuite / @AfterSuiteRun once for the whole run.
Good to know@Test(priority = 1, enabled = false, groups = "smoke")Order, skipping and grouping of tests.
Good to knowtestng.xmlA file to choose which tests to run, run in parallel, or pass parameters.
MethodWhat it doesExample
ImportantAssert.assertEquals(actual, expected)Fails if the two values differ. Note the order: actual first, expected second.Assert.assertEquals(driver.getTitle(), "Home");
ImportantAssert.assertTrue(condition) / assertFalseFails if the condition is not true / not false.Assert.assertTrue(btn.isEnabled());
ImportantAssert.assertEquals(actual, expected, "message")The message shows when the test fails. Always add one for clear reports.
Good to knowAssert.assertNotNull / assertNull / fail()Other checks.
Good to knowSoftAssertCollects ALL failures and reports them together when you call assertAll(). A normal Assert stops at the first failure.soft.assertAll();

The base class used in the three cases below

package selenium;​import org.openqa.selenium.WebDriver;import org.openqa.selenium.chrome.ChromeDriver;import org.openqa.selenium.chrome.ChromeOptions;import org.openqa.selenium.support.ui.WebDriverWait;import org.testng.annotations.AfterMethod;import org.testng.annotations.BeforeMethod;​import java.time.Duration;​public class BaseTest {​    protected WebDriver driver;    protected WebDriverWait wait;​    @BeforeMethod    public void setUp() {        ChromeOptions options = new ChromeOptions();        if (Boolean.getBoolean("headless")) {            options.addArguments("--headless=new");      // run without showing a window        }        options.addArguments("--window-size=1280,900");        driver = new ChromeDriver(options);               // opens Chrome        wait = new WebDriverWait(driver, Duration.ofSeconds(5));    }​    @AfterMethod(alwaysRun = true)    public void tearDown() {        if (driver != null) {            driver.quit();                                // closes the browser even if the test failed        }    }}
LinesWhat this part does
1-11Package and imports: classes borrowed from Selenium, TestNG and Java.
12-13The parent class of all test classes. Other classes say extends BaseTest and get the browser setup for free (inheritance).
14-16driver controls the browser. wait is a ready explicit wait of 5 seconds. They are protected so child classes can use them.
17-18TestNG runs this before every test method.
19-23Settings for Chrome. With -Dheadless=true Chrome runs without a window. The window size is fixed so the layout is the same on every machine.
24Starts Chrome. Selenium Manager downloads the right chromedriver if needed.
25-27Creates the wait: up to 5 seconds for any condition.
28-34Runs after every test, even if the test failed (alwaysRun = true). quit() closes the browser and ends the session.

11. Page Object Model (design pattern)

Good to knowPOM

Without POM, every test has locators inside it. If a locator changes, you edit many tests. In POM each page gets one class holding its locators and actions. Tests only call these actions. The three cases below keep things simple, so the locators and helper methods sit inside each test class. A real project would move them into page classes like this:

public class LoginPage {
    private final WebDriver driver;
    private final By email = By.id("email");
    private final By password = By.id("pass");
    private final By loginBtn = By.id("loginBtn");

    public LoginPage(WebDriver driver) { this.driver = driver; }

    public void loginAs(String user, String pass) {
        driver.findElement(email).sendKeys(user);
        driver.findElement(password).sendKeys(pass);
        driver.findElement(loginBtn).click();
    }
}
// in a test:  new LoginPage(driver).loginAs("qa@test.com", "Test@1234");

What it does: Locators are private and hidden (encapsulation). If the id changes, you fix one line.

12. Three real automations

Click a case below to open it. In each case you will see: the practice page, where every element is, the manual test cases, the full Java code with line numbers, and the code explained line by line. Read the manual test cases first, then the code. That is how real automation is done: manual thinking first, code second.

ImportantBefore you start

Case 1: Password validation (a full validation spec) Boundary values, equivalence partitions, negative and state testing on one password form.
Step 1: The practice page

Open the page, click around like a manual tester first, then look at the locators below.

Open demo-password.html

The test already points to this live page (PAGE_URL), so it runs from any computer with internet. The same file is in the repository at docs/demo-pages/demo-password.html.

ImportantWhat you will learn from this case

  • Data-driven tests (one method runs many times with different data)
  • Boundary value analysis and equivalence partitioning in real code
  • Checking one rule at a time
  • Reading attributes and the enabled / disabled state of a button
Step 2: Where each element is (locators)
ElementHTML on the pageBy.idCSS selectorXPath
Username box<input id="username" type="text">username#username//input[@id='username']
Password box<input id="password" type="password">passwordinput[type='password']//input[@id='password']
Confirm password box<input id="confirmPassword" type="password">confirmPassword#confirmPassword//label[text()='Confirm password']/following::input[1]
Show / Hide button<button id="togglePwd">Show</button>togglePwd#togglePwd//button[text()='Show']
One rule line<li id="rule-length" data-state="pending">rule-lengthli[data-state='pass']//li[@id='rule-length']
Strength text<span id="strength">Not entered</span>strength#strength//span[@id='strength']
Error message<div id="pwdError" class="error">pwdError#pwdError//div[@id='pwdError']
Create password button<button type="submit" id="submitBtn" disabled>submitBtnbutton[type='submit']//button[@type='submit']
Success message<div id="successMsg" hidden>successMsg#successMsg//div[contains(text(),'created successfully')]
Step 3: Manual test cases first

Automation follows manual testing. We first decide what to test, and only then write code.

IDTechnique usedInputExpected result
TC01State checkNothing typedButton disabled, strength "Not entered", rules pending
TC02-05Positive + boundaryAbcd@1234, Abc@1234 (8 chars), Abcdefgh@1234567 (16 chars), Str0ng@Passw0rdAll rules pass, Medium or Strong, button enabled, success message
TC06-07Boundary value7 characters, 17 charactersOnly the length rule fails
TC08-11Equivalence partitionNo uppercase, no lowercase, no digit, no special characterOnly that one rule fails
TC12Error guessingA space inside the passwordNo-space rule fails
TC13Business ruleUsername ubed, password MyUBED@123No-username rule fails
TC14-16Error guessing (hostile input)SQL text, script text, only spacesRejected, button disabled, no popup
TC17-19Negative: confirm passwordDifferent, different only in case, emptyError message, button disabled
TC20-21State transitionValid then invalid then valid; username changed laterButton enabled or disabled to match the data
TC22UI behaviourClick Show / HideField type switches between password and text
Step 4: The code

File: selenium/PasswordTest.java

package selenium;​import org.openqa.selenium.By;import org.openqa.selenium.WebElement;import org.testng.Assert;import org.testng.annotations.DataProvider;import org.testng.annotations.Test;import org.testng.asserts.SoftAssert;​public class PasswordTest extends BaseTest {​    // Live practice page hosted on GitHub Pages (works from any computer with internet)    private static final String PAGE_URL = "https://ubedzz49.github.io/qa-interview-prep/demo-pages/demo-password.html";​    private static final String[] ALL_RULES =            {"length", "upper", "lower", "number", "special", "nospace", "nouser"};​    // ---------- Locators: all in one place ----------    private final By username = By.id("username");    private final By password = By.id("password");    private final By confirm = By.id("confirmPassword");    private final By submitBtn = By.id("submitBtn");    private final By toggleBtn = By.id("togglePwd");    private final By strength = By.id("strength");    private final By pwdError = By.id("pwdError");    private final By confirmError = By.id("confirmError");    private final By successMsg = By.id("successMsg");​    // ---------- Helper methods ----------    private void open() {        driver.get(PAGE_URL);    }​    private void type(By locator, String text) {        WebElement field = driver.findElement(locator);        field.clear();        if (!text.isEmpty()) {            field.sendKeys(text);        }    }​    private void enter(String pwd, String confirmPwd, String user) {        type(username, user);        type(password, pwd);        type(confirm, confirmPwd);    }​    private void enter(String pwd) {        enter(pwd, pwd, "");    }​    private String ruleState(String rule) {        return driver.findElement(By.id("rule-" + rule)).getDomAttribute("data-state");    }​    private boolean buttonEnabled() {        return driver.findElement(submitBtn).isEnabled();    }​    // ---------- 1. Initial state ----------    @Test    public void tc01_initialState() {        open();        Assert.assertFalse(buttonEnabled(), "Button must be disabled at start");        Assert.assertEquals(driver.findElement(strength).getText(), "Not entered");        for (String rule : ALL_RULES) {            Assert.assertEquals(ruleState(rule), "pending", "Rule " + rule);        }        Assert.assertEquals(driver.findElement(pwdError).getText(), "");        Assert.assertFalse(driver.findElement(successMsg).isDisplayed());    }​    // ---------- 2. Positive cases ----------    @DataProvider(name = "positive")    public Object[][] positive() {        return new Object[][]{                // id,     technique,                       password,           expected strength                {"TC02", "Valid typical password",        "Abcd@1234",        "Medium"},                {"TC03", "Boundary: 8 chars (min)",       "Abc@1234",         "Medium"},                {"TC04", "Boundary: 16 chars (max)",      "Abcdefgh@1234567", "Strong"},                {"TC05", "Strong password (12+ chars)",   "Str0ng@Passw0rd",  "Strong"},        };    }​    @Test(dataProvider = "positive")    public void positiveCases(String id, String technique, String pwd, String expectedStrength) {        open();        enter(pwd);​        SoftAssert soft = new SoftAssert();                  // collect ALL failures, then report        for (String rule : ALL_RULES) {            soft.assertEquals(ruleState(rule), "pass", id + " rule " + rule);        }        soft.assertEquals(driver.findElement(strength).getText(), expectedStrength, id + " strength");        soft.assertEquals(driver.findElement(pwdError).getText(), "", id + " error text");        soft.assertTrue(buttonEnabled(), id + " button enabled");        soft.assertAll();​        driver.findElement(submitBtn).click();        Assert.assertEquals(driver.findElement(successMsg).getText(), "Password created successfully");    }​    // ---------- 3. Negative cases: break exactly ONE rule at a time ----------    @DataProvider(name = "negative")    public Object[][] negative() {        return new Object[][]{                // id,     technique,                              password,             username, broken rule                {"TC06", "Boundary: 7 chars (min - 1)",          "Abc@123",            "",     "length"},                {"TC07", "Boundary: 17 chars (max + 1)",         "Abcdefgh@12345678",  "",     "length"},                {"TC08", "Partition: no uppercase letter",       "abcd@1234",          "",     "upper"},                {"TC09", "Partition: no lowercase letter",       "ABCD@1234",          "",     "lower"},                {"TC10", "Partition: no number",                 "Abcd@efgh",          "",     "number"},                {"TC11", "Partition: no special character",      "Abcd12345",          "",     "special"},                {"TC12", "Error guessing: space inside",         "Abcd @1234",         "",     "nospace"},                {"TC13", "Business rule: contains username",     "MyUBED@123",         "ubed", "nouser"},        };    }​    @Test(dataProvider = "negative")    public void negativeCases(String id, String technique, String pwd, String user, String broken) {        open();        enter(pwd, pwd, user);​        SoftAssert soft = new SoftAssert();        for (String rule : ALL_RULES) {            String expected = rule.equals(broken) ? "fail" : "pass";            soft.assertEquals(ruleState(rule), expected, id + " rule " + rule);        }        soft.assertEquals(driver.findElement(strength).getText(), "Weak", id + " strength");        soft.assertEquals(driver.findElement(pwdError).getText(), "Password does not meet all the rules");        soft.assertFalse(buttonEnabled(), id + " button must be disabled");        soft.assertAll();    }​    // ---------- 4. Error guessing: hostile and strange input ----------    @Test    public void tc14_sqlInjectionTextIsRejected() {        open();        enter("' OR '1'='1");        Assert.assertFalse(buttonEnabled());        Assert.assertFalse(driver.findElement(successMsg).isDisplayed());    }​    @Test    public void tc15_scriptTextShowsNoPopup() {        open();        enter("<script>alert(1)</script>");        Assert.assertFalse(buttonEnabled());        // if the text had run as a script, an alert would be open now        try {            driver.switchTo().alert();            Assert.fail("An unexpected popup appeared");        } catch (org.openqa.selenium.NoAlertPresentException expected) {            // good: no popup        }    }​    @Test    public void tc16_onlySpacesAreRejected() {        open();        enter("        ");        Assert.assertEquals(ruleState("nospace"), "fail");        Assert.assertFalse(buttonEnabled());    }​    // ---------- 5. Confirm password ----------    @Test    public void tc17_mismatchShowsError() {        open();        enter("Abcd@1234", "Abcd@12345", "");        Assert.assertEquals(driver.findElement(confirmError).getText(), "Passwords do not match");        Assert.assertFalse(buttonEnabled());    }​    @Test    public void tc18_caseDifferenceIsMismatch() {        open();        enter("Abcd@1234", "abcd@1234", "");        Assert.assertEquals(driver.findElement(confirmError).getText(), "Passwords do not match");    }​    @Test    public void tc19_emptyConfirmKeepsButtonDisabled() {        open();        enter("Abcd@1234", "", "");        Assert.assertFalse(buttonEnabled());    }​    // ---------- 6. State transitions ----------    @Test    public void tc20_validInvalidValidChangesButton() {        open();        enter("Abcd@1234");        Assert.assertTrue(buttonEnabled());​        type(password, "abcd@1234");                         // breaks the uppercase rule        Assert.assertFalse(buttonEnabled());​        type(password, "Abcd@1234");        Assert.assertTrue(buttonEnabled());    }​    @Test    public void tc21_changingUsernameCanBreakValidPassword() {        open();        enter("Abcd@1234");        Assert.assertTrue(buttonEnabled());        type(username, "abcd");        Assert.assertEquals(ruleState("nouser"), "fail");        Assert.assertFalse(buttonEnabled());    }​    // ---------- 7. UI behaviour ----------    @Test    public void tc22_showHideTogglesFieldType() {        open();        WebElement field = driver.findElement(password);        Assert.assertEquals(field.getDomAttribute("type"), "password");        driver.findElement(toggleBtn).click();        Assert.assertEquals(field.getDomAttribute("type"), "text");        Assert.assertEquals(driver.findElement(toggleBtn).getText(), "Hide");        driver.findElement(toggleBtn).click();        Assert.assertEquals(field.getDomAttribute("type"), "password");    }}
Step 5: Explained line by line
LinesWhat this part does
1-9Package and imports. SoftAssert lets one test report all its failures together.
10-12Extends BaseTest, so driver and wait are ready. Every @Test method gets a fresh browser.
13-14Address of the practice page. Change PATH/TO to your folder.
15-17The seven rule names the page shows. We loop over this array instead of writing seven lines each time.
18-28Every element is found by its id and stored once as a By. If an id changes you fix one line.
29-33open() loads the page.
34-41type() finds the box, clear() removes old text, sendKeys() types the new text. Empty text is skipped.
42-51enter() fills username, password and confirm password. The shorter enter(pwd) below it is an overloaded shortcut.
52-55Reads the data-state attribute of a rule line. The page sets it to pass, fail or pending.
56-59Is the Create password button clickable? Uses isEnabled().
60-72TC01: before typing anything the button is disabled, strength says "Not entered", every rule is pending and there is no error or success text.
73-84Data provider with four valid passwords. Two of them are the boundaries 8 and 16 characters. One test method will run four times, once per row.
85-102Types the password, then checks all rules pass, the strength text, no error and an enabled button. SoftAssert reports every failed check together. Then it clicks the button and checks the success message.
103-118Eight invalid passwords. Each breaks exactly ONE rule: boundary 7 and 17, missing uppercase / lowercase / number / special, a space, and the username rule.
119-134For every rule it expects fail only for the broken rule and pass for all others. That proves the page flags the right rule and nothing else. It also checks "Weak", the error text and a disabled button.
135-165TC14: SQL text. TC15: script text; if it had run, an alert would be open, so switchTo().alert() would NOT throw NoAlertPresentException. TC16: only spaces.
166-188Different confirm password, a difference only in letter case, and an empty confirm password.
189-212The button must follow the data: valid, then invalid, then valid again. Changing the username later can also make a valid password invalid.
213-225The Show / Hide button changes the field type between password and text, and its own text between Show and Hide.
Step 6: Run it
mvn test -Dtest=PasswordTest                 // run this class only
mvn test -Dtest=PasswordTest -Dheadless=true   // run without a browser window
mvn test                                // run every test
Case 2: Full page validation (every kind of input) Text boxes, date, radio, checkbox, dropdowns, multi-select, slider, textarea, file upload and popups.
Step 1: The practice page

Open the page, click around like a manual tester first, then look at the locators below.

Open demo-form.html

The test already points to this live page (PAGE_URL), so it runs from any computer with internet. The same file is in the repository at docs/demo-pages/demo-form.html.

ImportantWhat you will learn from this case

  • Every input type and how to handle it
  • Dependent dropdowns and multi-select
  • Alert and confirm popups
  • File upload and boundary testing on file size
Step 2: Where each element is (locators)
ElementHTML on the pageBy.idCSS selectorXPath
Full name box<input id="fullName" type="text">fullName#fullName//input[@id='fullName']
Date of birth<input id="dob" type="date">dobinput[type='date']//input[@id='dob']
Male radio button<input type="radio" name="gender" id="genderMale" value="Male">genderMaleinput[name='gender'][value='Male']//input[@name='gender' and @value='Male']
Country dropdown<select id="country">countryselect#country//select[@id='country']
State dropdown (starts disabled)<select id="state" disabled>stateselect#state//select[@id='state']
Skills multi-select<select id="skills" multiple>skillsselect[multiple]//select[@multiple]
English checkbox<input type="checkbox" name="lang" id="langEnglish" value="English">langEnglishinput[name='lang']:checked//input[@name='lang' and @value='English']
Experience slider<input type="range" id="experience" min="0" max="20">experienceinput[type='range']//input[@type='range']
Address textarea<textarea id="address" maxlength="200">addresstextarea#address//textarea[@id='address']
Resume file input<input type="file" id="resume">resumeinput[type='file']//input[@type='file']
Error under a field<div class="error" id="err-email">err-email#err-email//div[@id='err-email']
Submit / Reset buttons<button type="submit" id="submitBtn">Submit</button>submitBtn / resetBtnbutton[type='submit']//button[text()='Reset']
Step 3: Manual test cases first

Automation follows manual testing. We first decide what to test, and only then write code.

IDControlWhat is testedTechnique
TC01-02Whole formStart state; submit empty form shows 11 error messagesState check, required fields
TC03Name, email, phone (text)Length 1, 2, 40, 41; letters only; email formats; phone length and first digitBoundary value, equivalence partition
TC04Date of birth (date)Exactly 18 years, one day short, one day over, future dateBoundary value
TC05Gender (radio)Only one can be selected at a timeState check
TC06-07Country and State (dependent dropdowns)State list changes with country; disabled again when country is clearedDependency check
TC08Skills (multi-select)1 to 3 allowed; 4 and 0 give errorsBoundary value
TC09Languages (checkboxes)Tick and untick, count of ticked boxesState check
TC10Experience (slider)Value display at 12 and at maximum 20Boundary value
TC11-12Address (textarea)Counter, 200 character limit, minimum 10 charactersBoundary value
TC13Resume (file upload)PDF, PNG, exactly 2 MB, 2 MB + 1 byte, empty file, cv.pdf.exeBoundary value, error guessing
TC14Terms (checkbox)Must be tickedRequired field
TC15SubmitFull valid form, alert popup, result textPositive end-to-end
TC16ResetConfirm popup: Cancel keeps data, OK clears itPopup handling
Step 4: The code

File: selenium/FormTest.java

package selenium;​import org.openqa.selenium.Alert;import org.openqa.selenium.By;import org.openqa.selenium.JavascriptExecutor;import org.openqa.selenium.WebElement;import org.openqa.selenium.support.ui.ExpectedConditions;import org.openqa.selenium.support.ui.Select;import org.testng.Assert;import org.testng.annotations.DataProvider;import org.testng.annotations.Test;​import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.time.LocalDate;import java.util.Arrays;import java.util.List;import java.util.stream.Collectors;​public class FormTest extends BaseTest {​    // Live practice page hosted on GitHub Pages (works from any computer with internet)    private static final String PAGE_URL = "https://ubedzz49.github.io/qa-interview-prep/demo-pages/demo-form.html";    private static final int MB = 1024 * 1024;​    // ---------- Helper methods ----------    private void open() {        driver.get(PAGE_URL);    }​    private void type(String id, String text) {        WebElement field = driver.findElement(By.id(id));        field.clear();        field.sendKeys(text);    }​    private void clickSubmit() {        driver.findElement(By.id("submitBtn")).click();    }​    private String error(String field) {        return driver.findElement(By.id("err-" + field)).getText();    }​    // Date and range inputs are awkward to type into, so we set them with JavaScript    private void setValueWithJs(String id, String value) {        WebElement el = driver.findElement(By.id(id));        ((JavascriptExecutor) driver).executeScript(                "arguments[0].value = arguments[1];" +                "arguments[0].dispatchEvent(new Event('input', { bubbles: true }));", el, value);    }​    // Create a fake file of a given name and size, return its full path    private String makeFile(String name, int sizeInBytes) throws IOException {        Path folder = Files.createTempDirectory("upload");        Path file = folder.resolve(name);        Files.write(file, new byte[sizeInBytes]);        return file.toString();    }​    // ---------- 1. Initial state ----------    @Test    public void tc01_initialState() {        open();        Assert.assertFalse(driver.findElement(By.id("state")).isEnabled());        Assert.assertEquals(driver.findElement(By.id("expOut")).getText(), "0");        Assert.assertEquals(driver.findElement(By.id("addrCount")).getText(), "0");        Assert.assertEquals(driver.findElements(By.cssSelector("input[name='gender']:checked")).size(), 0);        Assert.assertFalse(driver.findElement(By.id("terms")).isSelected());        Assert.assertFalse(driver.findElement(By.id("result")).isDisplayed());    }​    // ---------- 2. Empty form: every required field shows an error ----------    @Test    public void tc02_emptyFormShowsAllErrors() {        open();        clickSubmit();        String[][] expected = {                {"fullName", "Full name is required"},                {"email", "Email is required"},                {"phone", "Mobile number is required"},                {"dob", "Date of birth is required"},                {"gender", "Select a gender"},                {"country", "Select a country"},                {"skills", "Select at least one skill"},                {"lang", "Select at least one language"},                {"address", "Address is required"},                {"resume", "Upload your resume"},                {"terms", "You must accept the terms"},        };        for (String[] row : expected) {            Assert.assertEquals(error(row[0]), row[1], "Field " + row[0]);        }    }​    // ---------- 3. Text inputs: data-driven validation ----------    @DataProvider(name = "textCases")    public Object[][] textCases() {        String phoneMsg = "Enter a 10 digit number starting with 6, 7, 8 or 9";        String nameLenMsg = "Name must be 2 to 40 characters";        return new Object[][]{                // field,      value,              expected message,  why                {"fullName", "A",                 nameLenMsg, "Boundary: 1 char (min - 1)"},                {"fullName", "Al",                "",         "Boundary: 2 chars (min)"},                {"fullName", "A".repeat(40),      "",         "Boundary: 40 chars (max)"},                {"fullName", "A".repeat(41),      nameLenMsg, "Boundary: 41 chars (max + 1)"},                {"fullName", "John3",             "Name can have only letters, space, dot, hyphen and apostrophe", "Invalid: digit in name"},                {"fullName", "Mary-Ann O'Neil",   "",         "Valid special characters"},                {"email",    "abc",               "Enter a valid email address", "Invalid: no @"},                {"email",    "abc@mail",          "Enter a valid email address", "Invalid: no domain extension"},                {"email",    "a b@mail.com",      "Enter a valid email address", "Invalid: space"},                {"email",    "user@mail.com",     "",         "Valid email"},                {"phone",    "12345",             phoneMsg,   "Invalid: too short"},                {"phone",    "5876543210",        phoneMsg,   "Invalid: starts with 5"},                {"phone",    "98765432101",       phoneMsg,   "Boundary: 11 digits"},                {"phone",    "abcdefghij",        phoneMsg,   "Invalid: letters"},                {"phone",    "9876543210",        "",         "Valid mobile number"},        };    }​    @Test(dataProvider = "textCases")    public void tc03_textFieldValidation(String field, String value, String message, String why) {        open();        type(field, value);        clickSubmit();        Assert.assertEquals(error(field), message, why);    }​    // ---------- 4. Date of birth: boundary around 18 years ----------    @DataProvider(name = "dobCases")    public Object[][] dobCases() {        LocalDate today = LocalDate.now();        return new Object[][]{                {"exactly 18 years today (valid)",  today.minusYears(18).toString(),            ""},                {"turns 18 tomorrow (invalid)",     today.minusYears(18).plusDays(1).toString(), "You must be at least 18 years old"},                {"18 years and 1 day (valid)",      today.minusYears(18).minusDays(1).toString(), ""},                {"tomorrow (future date)",          today.plusDays(1).toString(),               "Date of birth cannot be in the future"},        };    }​    @Test(dataProvider = "dobCases")    public void tc04_dateOfBirth(String why, String date, String message) {        open();        setValueWithJs("dob", date);        clickSubmit();        Assert.assertEquals(error("dob"), message, why);    }​    // ---------- 5. Radio buttons ----------    @Test    public void tc05_onlyOneRadioCanBeSelected() {        open();        driver.findElement(By.id("genderFemale")).click();        Assert.assertTrue(driver.findElement(By.id("genderFemale")).isSelected());        driver.findElement(By.id("genderOther")).click();        Assert.assertTrue(driver.findElement(By.id("genderOther")).isSelected());        Assert.assertFalse(driver.findElement(By.id("genderFemale")).isSelected());        Assert.assertEquals(driver.findElements(By.cssSelector("input[name='gender']:checked")).size(), 1);    }​    // ---------- 6. Dropdowns: single and dependent ----------    private List<String> optionTexts(Select select) {        return select.getOptions().stream().map(WebElement::getText).collect(Collectors.toList());    }​    @Test    public void tc06_countryChangesStateList() {        open();        Select country = new Select(driver.findElement(By.id("country")));        WebElement stateElement = driver.findElement(By.id("state"));​        country.selectByVisibleText("India");        Assert.assertTrue(stateElement.isEnabled());        Assert.assertEquals(optionTexts(new Select(stateElement)),                Arrays.asList("-- Select state --", "Karnataka", "Maharashtra", "Delhi"));​        country.selectByValue("USA");        Assert.assertEquals(optionTexts(new Select(stateElement)),                Arrays.asList("-- Select state --", "California", "Texas"));​        country.selectByIndex(0);                            // back to "-- Select country --"        Assert.assertFalse(stateElement.isEnabled());    }​    @Test    public void tc07_stateIsRequiredAfterChoosingCountry() {        open();        new Select(driver.findElement(By.id("country"))).selectByValue("UK");        clickSubmit();        Assert.assertEquals(error("state"), "Select a state");    }​    // ---------- 7. Multi-select ----------    @Test    public void tc08_multiSelectAllowsOneToThreeSkills() {        open();        Select skills = new Select(driver.findElement(By.id("skills")));        Assert.assertTrue(skills.isMultiple());​        skills.selectByValue("Selenium");        skills.selectByValue("Git");        clickSubmit();        Assert.assertEquals(error("skills"), "");​        skills.selectByValue("Playwright");        skills.selectByValue("SQL");                         // now 4 selected = max + 1        clickSubmit();        Assert.assertEquals(error("skills"), "Select at most 3 skills");​        skills.deselectAll();        clickSubmit();        Assert.assertEquals(error("skills"), "Select at least one skill");    }​    // ---------- 8. Checkboxes ----------    @Test    public void tc09_languageCheckboxes() {        open();        driver.findElement(By.id("langEnglish")).click();        driver.findElement(By.id("langTelugu")).click();        Assert.assertEquals(driver.findElements(By.cssSelector("input[name='lang']:checked")).size(), 2);        driver.findElement(By.id("langEnglish")).click();      // click again = untick        Assert.assertEquals(driver.findElements(By.cssSelector("input[name='lang']:checked")).size(), 1);    }​    // ---------- 9. Slider ----------    @Test    public void tc10_sliderShowsItsValue() {        open();        setValueWithJs("experience", "12");        Assert.assertEquals(driver.findElement(By.id("expOut")).getText(), "12");        setValueWithJs("experience", "20");                    // max        Assert.assertEquals(driver.findElement(By.id("expOut")).getText(), "20");    }​    // ---------- 10. Textarea ----------    @Test    public void tc11_textareaCounterAndLimit() {        open();        WebElement box = driver.findElement(By.id("address"));        box.sendKeys("Hello");        Assert.assertEquals(driver.findElement(By.id("addrCount")).getText(), "5");​        box.clear();        box.sendKeys("x".repeat(205));                         // we type 205, the browser stops at 200        Assert.assertEquals(box.getAttribute("value").length(), 200);        Assert.assertEquals(driver.findElement(By.id("addrCount")).getText(), "200");    }​    @Test    public void tc12_shortAddressIsRejected() {        open();        type("address", "Short");        clickSubmit();        Assert.assertEquals(error("address"), "Address must be at least 10 characters");    }​    // ---------- 11. File upload ----------    @DataProvider(name = "fileCases")    public Object[][] fileCases() {        return new Object[][]{                // why,                      file name,      size,        expected message                {"valid PDF",               "resume.pdf",  1000,        ""},                {"wrong type (png)",        "photo.png",   1000,        "Only PDF, DOC or DOCX files are allowed"},                {"exactly 2 MB (max)",      "big.pdf",     2 * MB,      ""},                {"2 MB + 1 byte (max + 1)", "bigger.pdf",  2 * MB + 1,  "File must be 2 MB or smaller"},                {"empty file",              "empty.pdf",   0,           "File is empty"},                {"double extension",        "cv.pdf.exe",  1000,        "Only PDF, DOC or DOCX files are allowed"},        };    }​    @Test(dataProvider = "fileCases")    public void tc13_resumeUpload(String why, String name, int size, String message) throws IOException {        open();        // For a file input we do NOT click. We send the file path as text.        driver.findElement(By.id("resume")).sendKeys(makeFile(name, size));        clickSubmit();        Assert.assertEquals(error("resume"), message, why);    }​    // ---------- 12. Terms checkbox ----------    @Test    public void tc14_termsMustBeAccepted() {        open();        clickSubmit();        Assert.assertEquals(error("terms"), "You must accept the terms");        driver.findElement(By.id("terms")).click();        clickSubmit();        Assert.assertEquals(error("terms"), "");    }​    // ---------- 13. Happy path: whole form, plus the alert popup ----------    @Test    public void tc15_validFormSubmitsAndShowsResult() throws IOException {        open();        type("fullName", "Ravi Kumar");        type("email", "ravi@example.com");        type("phone", "9876543210");        setValueWithJs("dob", "1998-05-20");        driver.findElement(By.id("genderMale")).click();​        new Select(driver.findElement(By.id("country"))).selectByVisibleText("India");        new Select(driver.findElement(By.id("state"))).selectByVisibleText("Karnataka");​        Select skills = new Select(driver.findElement(By.id("skills")));        skills.selectByValue("Selenium");        skills.selectByValue("Playwright");​        driver.findElement(By.id("langEnglish")).click();        driver.findElement(By.id("langHindi")).click();        setValueWithJs("experience", "2");        type("address", "12 MG Road, Bengaluru");        driver.findElement(By.id("resume")).sendKeys(makeFile("ravi.pdf", 500));        driver.findElement(By.id("terms")).click();​        clickSubmit();​        // The page shows an alert popup. We must handle it before doing anything else.        Alert alert = wait.until(ExpectedConditions.alertIsPresent());        Assert.assertEquals(alert.getText(), "Application submitted successfully");        alert.accept();​        String json = driver.findElement(By.id("result")).getText();        Assert.assertTrue(json.contains("\"fullName\": \"Ravi Kumar\""));        Assert.assertTrue(json.contains("\"phone\": \"9876543210\""));        Assert.assertTrue(json.contains("\"state\": \"Karnataka\""));        Assert.assertTrue(json.contains("\"experience\": 2"));        Assert.assertTrue(json.contains("\"resume\": \"ravi.pdf\""));    }​    // ---------- 14. Reset button and the confirm popup ----------    @Test    public void tc16_resetAsksForConfirmation() {        open();        type("fullName", "Ravi Kumar");        new Select(driver.findElement(By.id("country"))).selectByValue("USA");​        driver.findElement(By.id("resetBtn")).click();        Alert confirm = wait.until(ExpectedConditions.alertIsPresent());        Assert.assertEquals(confirm.getText(), "Clear the whole form?");        confirm.dismiss();                                     // press Cancel        Assert.assertEquals(driver.findElement(By.id("fullName")).getAttribute("value"), "Ravi Kumar");​        driver.findElement(By.id("resetBtn")).click();        wait.until(ExpectedConditions.alertIsPresent()).accept();   // press OK        Assert.assertEquals(driver.findElement(By.id("fullName")).getAttribute("value"), "");        Assert.assertFalse(driver.findElement(By.id("state")).isEnabled());    }}
Step 5: Explained line by line
LinesWhat this part does
1-20Imports. Note Select, Alert, JavascriptExecutor, LocalDate and file classes. Each is used for one type of input.
21-26Extends BaseTest. PAGE_URL is the page address. MB is the number of bytes in 1 MB, used for the file size boundaries.
27-46Small helpers: open(), type(), clickSubmit() and error(field), which reads the red message under a field (err-<field>).
47-54A date box and a slider are hard to type into. This helper sets the value with JavaScript, then fires an input event so the page reacts as if a user did it.
55-61Creates a real temporary file with a given name and size. A file input needs a real path on disk.
62-73TC01: the State dropdown is disabled, counters show 0, no radio selected, terms not ticked, result hidden.
74-96TC02: click Submit on an empty form. A 2D array holds each field and its expected message, and a loop checks all 11.
97-121Data provider with 15 rows: boundaries on name length (1, 2, 40, 41), invalid characters, email formats, phone length and first digit. An empty expected message means "no error expected".
122-129One method for all 15 rows: type the value into the field whose id is field, click Submit, compare the error text.
130-149Dates are calculated from today with LocalDate, so the test never goes out of date. Boundary: exactly 18 years, one day too young, one day older, and a future date.
150-161Click a radio, check isSelected(). Picking another one must unselect the first. A CSS selector with :checked counts selected radios.
162-193Select wraps a <select>. This is a dependent dropdown: changing the country changes the list of states. getOptions() reads the list; selectByIndex(0) returns to the first option and the State box becomes disabled again.
194-215isMultiple() confirms the list allows many choices. Select 2 (valid), then 4 (max + 1, error), then deselectAll() (0, error).
216-226Clicking a checkbox toggles it. Count of ticked boxes uses the CSS :checked.
227-236The slider value is set with JavaScript and the shown number is checked, including the maximum 20.
237-258Types "Hello" and checks the counter. Then types 205 characters: the browser keeps only 200 (maxlength). Another test checks the 10 character minimum.
259-281Six cases with name and size. Boundary: exactly 2 MB passes, 2 MB + 1 byte fails. Also a wrong type, an empty file, and a double extension (cv.pdf.exe).
282-292Without ticking the terms an error shows. After ticking it, the error clears.
293-331Fills every control with valid data. After Submit the page shows an alert; alertIsPresent() waits for it, getText() reads it, accept() presses OK. Then the result text is checked.
332-350Reset shows a confirm popup. dismiss() (Cancel) keeps the data. accept() (OK) clears the form and disables the State box.
Step 6: Run it
mvn test -Dtest=FormTest                 // run this class only
mvn test -Dtest=FormTest -Dheadless=true   // run without a browser window
mvn test                                // run every test
Case 3: Complete flow (login to order confirmation) One user journey: login, search, cart, coupon, checkout, payment and logout.
Step 1: The practice page

Open the page, click around like a manual tester first, then look at the locators below.

Open demo-shop.html

The test already points to this live page (PAGE_URL), so it runs from any computer with internet. The same file is in the repository at docs/demo-pages/demo-shop.html.

ImportantWhat you will learn from this case

  • An end-to-end test with many steps
  • Checking calculations (subtotal, discount, total)
  • Negative cases inside a flow
  • Finding elements inside a card or table row
Step 2: Where each element is (locators)
ElementHTML on the pageBy.idCSS selectorXPath
Login email box<input id="email" type="email">email#email//input[@id='email']
Login button<button type="button" id="loginBtn">Login</button>loginBtn#loginBtn//button[text()='Login']
Search box<input id="search" placeholder="Search products">searchinput[placeholder='Search products']//input[@placeholder='Search products']
A product card<div class="product" id="product-2">product-2.product//div[@id='product-2']
Add button inside a card<button id="add-3">Add to cart</button>add-3#product-3 button//div[@id='product-3']//button
Cart link<button id="cartLink">Cart (<span id="cartCount">0</span>)</button>cartLink#cartLink//button[contains(text(),'Cart')]
Quantity of an item<span id="qty-1">2</span>qty-1#cart-row-1 span//tr[@id='cart-row-1']//span
Remove button<button id="rm-3">Remove</button>rm-3#rm-3//tr[@id='cart-row-3']//button[text()='Remove']
Coupon box<input id="couponInput" aria-label="Coupon code">couponInput#couponInput//input[@id='couponInput']
Total amount<strong id="total">total#total//strong[@id='total']
UPI radio button<input type="radio" name="pay" id="payUpi" value="UPI">payUpiinput[name='pay'][value='UPI']//input[@name='pay' and @value='UPI']
Order id<strong id="orderId">orderId#orderId//strong[@id='orderId']
Step 3: Manual test cases first

Automation follows manual testing. We first decide what to test, and only then write code.

StepActionExpected resultIdea
Login (4 cases)Empty fields, wrong password, unknown email, password in wrong caseCorrect error message, shop page stays hiddenNegative testing
1Login with qa@test.com and Test@1234Welcome text, 3 products shownPositive
2Search "pen", "zzz"; filter by category1 product, "No products found", counts correctSearch and filter
3Add Notebook twice and Backpack onceCart count is 3State change
4Open the cartNotebook qty 2, line total 500, subtotal 1700Calculation check
5Click + then -Quantity 3 then back to 2State change
6Coupon ABC, then SAVE10Invalid message; then discount 170, total 1530Negative then positive
7Remove BackpackSubtotal 500, discount 50, total 450Recalculation
8Checkout with empty fields, PIN 123, no paymentThree different error messagesValidation
9Pay with UPIOrder placed, id like ORD-12345, total 450, cart emptiedEnd result
10LogoutBack to login pageFlow end
ExtraOpen cart with no itemsCheckout button disabledRule check
Step 4: The code

File: selenium/ShopFlowTest.java

package selenium;​import org.openqa.selenium.By;import org.openqa.selenium.Keys;import org.openqa.selenium.WebElement;import org.openqa.selenium.support.ui.ExpectedConditions;import org.openqa.selenium.support.ui.Select;import org.testng.Assert;import org.testng.annotations.DataProvider;import org.testng.annotations.Test;​public class ShopFlowTest extends BaseTest {​    // Live practice page hosted on GitHub Pages (works from any computer with internet)    private static final String PAGE_URL = "https://ubedzz49.github.io/qa-interview-prep/demo-pages/demo-shop.html";    private static final String EMAIL = "qa@test.com";    private static final String PASSWORD = "Test@1234";​    // ---------- Helper methods ----------    private void type(String id, String text) {        WebElement field = driver.findElement(By.id(id));        field.clear();        field.sendKeys(text);    }​    private void click(String id) {        driver.findElement(By.id(id)).click();    }​    private String text(String id) {        return driver.findElement(By.id(id)).getText();    }​    private void login(String email, String password) {        type("email", email);        type("pass", password);        click("loginBtn");    }​    // "₹1530" -> 1530    private int money(String id) {        return Integer.parseInt(text(id).replace("₹", ""));    }​    private int productCount() {        return driver.findElements(By.cssSelector(".product")).size();    }​    // Empty a text box by pressing Backspace (this fires the key events the page listens to)    private void emptyBox(String id) {        WebElement box = driver.findElement(By.id(id));        int length = box.getAttribute("value").length();        for (int i = 0; i < length; i++) {            box.sendKeys(Keys.BACK_SPACE);        }    }​    // ---------- Login: negative cases ----------    @DataProvider(name = "badLogins")    public Object[][] badLogins() {        return new Object[][]{                {"both empty",             "",                 "",         "Email and password are required"},                {"wrong password",         EMAIL,              "wrong",    "Invalid email or password"},                {"unknown email",          "nobody@test.com",  PASSWORD,   "Invalid email or password"},                {"password in wrong case", EMAIL,              "test@1234", "Invalid email or password"},        };    }​    @Test(dataProvider = "badLogins")    public void loginFails(String why, String email, String password, String message) {        driver.get(PAGE_URL);        login(email, password);        Assert.assertEquals(text("loginError"), message, why);        Assert.assertFalse(driver.findElement(By.id("shopView")).isDisplayed());    }​    // ---------- Complete end-to-end flow ----------    @Test    public void endToEndPurchase() {        driver.get(PAGE_URL);​        // 1. Login with valid details        login(EMAIL, PASSWORD);        wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("shopView")));        Assert.assertEquals(text("welcome"), "Welcome, qa@test.com");        Assert.assertEquals(productCount(), 3);​        // 2. Search and filter products        type("search", "pen");        Assert.assertEquals(productCount(), 1);        Assert.assertEquals(driver.findElement(By.cssSelector("#product-2 .name")).getText(), "Pen Pack");​        emptyBox("search");        type("search", "zzz");        Assert.assertTrue(driver.findElement(By.id("noResults")).isDisplayed());​        emptyBox("search");        Select category = new Select(driver.findElement(By.id("category")));        category.selectByVisibleText("Bags");        Assert.assertEquals(productCount(), 1);        category.selectByValue("All");        Assert.assertEquals(productCount(), 3);​        // 3. Add products to the cart        click("add-1");                                  // Notebook        click("add-1");                                  // Notebook again, so quantity = 2        driver.findElement(By.xpath("//div[@id='product-3']//button")).click();   // Backpack, found with XPath        Assert.assertEquals(text("cartCount"), "3");​        // 4. Check the cart and the maths        click("cartLink");        Assert.assertEquals(text("qty-1"), "2");        Assert.assertEquals(driver.findElement(By.cssSelector("#cart-row-1 .line-total")).getText(), "₹500");        Assert.assertEquals(money("subtotal"), 500 + 1200);        Assert.assertEquals(money("discount"), 0);        Assert.assertEquals(money("total"), 1700);​        // 5. Change quantity        click("inc-1");        Assert.assertEquals(text("qty-1"), "3");        click("dec-1");        Assert.assertEquals(text("qty-1"), "2");​        // 6. Coupon: invalid first, then valid        type("couponInput", "ABC");        click("applyCoupon");        Assert.assertEquals(text("couponMsg"), "Invalid coupon code");        Assert.assertEquals(money("discount"), 0);​        type("couponInput", "SAVE10");        click("applyCoupon");        Assert.assertEquals(text("couponMsg"), "Coupon SAVE10 applied: 10% off");        Assert.assertEquals(money("discount"), 170);        Assert.assertEquals(money("total"), 1530);​        // 7. Remove an item and the total updates        click("rm-3");                                   // remove Backpack        Assert.assertEquals(driver.findElements(By.id("cart-row-3")).size(), 0);        Assert.assertEquals(money("subtotal"), 500);        Assert.assertEquals(money("discount"), 50);        Assert.assertEquals(money("total"), 450);​        // 8. Checkout: validation first        click("checkoutBtn");        click("placeOrder");        Assert.assertEquals(text("checkoutError"), "Name and address are required");​        type("shipName", "Ravi Kumar");        type("shipAddress", "12 MG Road, Bengaluru");        type("pin", "123");        click("placeOrder");        Assert.assertEquals(text("checkoutError"), "Enter a valid 6 digit PIN code");​        type("pin", "560001");        click("placeOrder");        Assert.assertEquals(text("checkoutError"), "Select a payment method");​        // 9. Pay and confirm the order        click("payUpi");        click("placeOrder");        wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("doneView")));        Assert.assertEquals(text("orderStatus"), "Order placed");        Assert.assertTrue(text("orderId").matches("ORD-\\d{5}"), "Order id format");        Assert.assertEquals(text("orderTotal"), "₹450");        Assert.assertEquals(text("orderPay"), "UPI");        Assert.assertEquals(text("cartCount"), "0");     // cart is emptied​        // 10. Logout returns to the login page        click("logoutBtn");        Assert.assertTrue(driver.findElement(By.id("loginView")).isDisplayed());        Assert.assertFalse(driver.findElement(By.id("topNav")).isDisplayed());    }​    // ---------- Empty cart rule ----------    @Test    public void checkoutIsDisabledForEmptyCart() {        driver.get(PAGE_URL);        login(EMAIL, PASSWORD);        click("cartLink");        Assert.assertTrue(driver.findElement(By.id("emptyCart")).isDisplayed());        Assert.assertFalse(driver.findElement(By.id("checkoutBtn")).isEnabled());    }}
Step 5: Explained line by line
LinesWhat this part does
1-11Imports. Keys is used to press Backspace.
12-18Extends BaseTest. The page address and the valid login are constants.
19-40Short helpers so the test reads like a story: type, click, text (visible text by id), and login.
41-49money("total") turns the text "₹1530" into the number 1530 so we can compare numbers.
50-57Empties a box by pressing Backspace. This fires the keyboard events the search box listens to.
58-68Four wrong logins: both empty, wrong password, unknown email, and the password in the wrong letter case.
69-76Each row logs in with bad data, then checks the exact error text and that the shop page stays hidden.
77-81One long test is one full user journey. The numbered comments are the steps.
82-87Log in, wait until the shop page is visible (explicit wait), check the welcome text and 3 products.
88-103Search "pen" shows 1 product; a CSS selector with a space (#product-2 .name) finds the name inside the card. "zzz" shows "No products found". The Select class filters by category.
104-109Add the Notebook twice. The Backpack button is found with XPath: the button inside the div whose id is product-3.
110-117Open the cart and check quantity 2, the line total, and that subtotal 1700 = 500 + 1200.
118-123Plus makes quantity 3, minus brings it back to 2.
124-135A wrong coupon gives an error and no discount. SAVE10 gives 10% off: discount 170, total 1530.
136-142Remove the Backpack. The row must be gone (findElements size 0) and the totals recalculate: 500, 50, 450.
143-157Three validation messages in order: empty fields, wrong PIN 123, no payment method.
158-167Choose UPI and place the order. Check the status, the order id with a regular expression (ORD- and 5 digits), the amount, and that the cart is empty.
168-173Logout returns to the login page and hides the top menu.
174-183A separate small test: with an empty cart the Checkout button must be disabled.
Step 6: Run it
mvn test -Dtest=ShopFlowTest                 // run this class only
mvn test -Dtest=ShopFlowTest -Dheadless=true   // run without a browser window
mvn test                                // run every test

13. Quick interview answers