WebDriver, WebElement, and the Test Lifecycle

In the previous article, you built a local lab and ran one TestNG test. Chrome opened, TestKru loaded, an assertion passed, and quit() closed the browser.

That test worked, but some of it may still feel unclear. Which line started the browser? Which object typed the text? Why did the browser close at the end?

This lesson answers those questions. You will name the two objects behind almost every Selenium Java line: WebDriver and WebElement. Then you will learn the test lifecycle: setup, act, assert, and teardown. That order keeps tests readable and stops leftover browser windows from piling up on your machine.

Selenium Java gives you many types, but a beginner test usually needs only two.

WebDriver represents the browser session. It opens URLs, reports the page title, searches the page, and ends the session when you are done.

WebElement represents one single control inside the loaded page, such as a text box, a button, a link, or a heading. It types, clicks, reads text, and reports state.

If you want one short picture to hold on to: WebDriver controls the browser window, and a WebElement is one field or button inside that window.

Selenium conceptRole in your testExample line
WebDriverOwns one browser session and drives the whole browserdriver.get("https://testkru.com/Elements/TextFields")
WebElementPoints to one HTML control inside the loaded pagelastName.sendKeys("codekru")
findElement(By ...)The bridge that turns a locator into a WebElementdriver.findElement(By.id("lastNameWithPlaceholder"))
@BeforeMethodSetup step that creates the sessiondriver = new ChromeDriver();
AssertDecides pass or failAssert.assertEquals(actual, "codekru")
@AfterMethodTeardown step that ends the sessiondriver.quit();

You already used the first two objects in the previous article without naming them. ChromeDriver is a WebDriver. When you call findElement(...), Selenium hands back a WebElement. Making that relationship explicit now is what makes the upcoming navigation and locator lessons feel natural instead of memorized.

In Java, WebDriver is an interface in the Selenium API. An interface is a contract: it lists the commands that must exist, without saying how any particular browser carries them out. Concrete classes implement those commands for a specific browser.

For local Chrome, you usually write:

WebDriver driver = new ChromeDriver();

Read that line from right to left:

  • new ChromeDriver() starts Chrome and the ChromeDriver process that talks to it.
  • WebDriver is the type your test code depends on.
  • driver is just a variable name. It is a strong convention across the Selenium world, not a Selenium rule.

Why declare the variable as WebDriver instead of ChromeDriver? Because almost every later line only needs “a browser session.” Declaring the interface keeps that intent clear. Later, if you switch to Firefox, Edge, or a Selenium Grid session, you change how you create the driver, not every line that uses it.

Official Selenium Java documentation treats WebDriver as the main entry point for controlling a browser: open pages, find elements, switch windows, and end the session. Anything that affects the browser as a whole belongs to WebDriver.

When new ChromeDriver() succeeds, Selenium starts a session. A session is the live link between your Java test and one controlled browser. Everything you do afterwards travels over that link.

Here is the rough flow for a local Chrome test:

Your Java test
      │
      ▼
WebDriver API (ChromeDriver)
      │
      ▼
ChromeDriver process
      │
      ▼
Chrome browser (one session)

Your test does not control Chrome by itself. It sends a command through the WebDriver API, the driver process talks to the browser, and the browser reports back.

Until that session ends, later commands belong to the same browser. When you call driver.get(...) again, you are not starting a new browser. You are sending another command on the open session, so the same window navigates to a new URL.

That is why cleanup matters. If a test crashes before quit(), the session and browser can stay open. A clean lifecycle ends the session on purpose.

You do not need every WebDriver method yet. For this lesson, these are enough.

MethodWhat it does for you
get(String url)Opens a URL in the current browser window
getTitle()Returns the page title
getCurrentUrl()Returns the current URL
findElement(By locator)Finds the first matching element and returns a WebElement
findElements(By locator)Finds all matches and returns a list, which is empty when nothing matches
close()Closes the current window
quit()Ends the session and closes associated windows

Notice the pattern in that list. Every one of those methods is about the browser as a whole or about starting a search. None of them types text or clicks anything, because typing and clicking belong to a single control, and that is the job of the next object.

A WebElement represents one HTML element in the page: an input, a button, a link, a heading, or a table cell. Official Selenium documentation says most page interactions go through this interface: click, type, clear, read text, and check state.

A WebElement is not the HTML source of that element. It is a handle: a reference Selenium uses to reach that exact element in the live browser. As long as the element is still on the page, methods on that handle talk to the real control. If the page changes and that node is replaced, the old handle can stop working (more on that below).

You never build a WebElement with new. You ask WebDriver, or another WebElement, to find it for you:

WebElement lastName = driver.findElement(By.id("lastNameWithPlaceholder"));

What happens when that line runs:

  1. Your test sends a find command through the WebDriver session.
  2. The browser searches the current page for a match.
  3. If a match is found, Selenium returns a WebElement reference to it.
  4. If nothing matches, and no wait rescues you, Selenium throws NoSuchElementException.

Notice the two halves of the line. By.id("lastNameWithPlaceholder") is the locator (what to look for). findElement runs the search. Module 2 teaches how to write stable locators. For now, unique ids are the friendliest starting point.

On https://testkru.com/Elements/TextFields, the last name field has id="lastNameWithPlaceholder", so By.id("lastNameWithPlaceholder") is a clear beginner locator for this lesson.

MethodPlain-English job
click()Clicks the element
sendKeys("text")Types into an input or editable field
clear()Clears an input’s value when supported
getText()Reads visible text for many elements
getAttribute("value")Reads an attribute, such as an input’s value
isDisplayed()Returns whether the element is shown
isEnabled()Returns whether the element can be interacted with
isSelected()Returns whether a checkbox, radio, or option is selected

Module 3 gives these methods a full lesson each with realistic forms and dynamic components. For now, hold on to the split in responsibilities:

  • WebDriver owns the browser session and performs the search.
  • WebElement performs the action on the one control that the search returned.

Almost every beginner interaction is the same three steps in the same order.

  1. WebDriver opens the page with get.
  2. WebDriver finds the control with findElement and hands back a WebElement.
  3. The WebElement performs the action with sendKeys, click, and similar methods.
driver.get(url)
        │
        ▼
driver.findElement(locator)  --->  WebElement
                                        │
                                        ▼
                               element.sendKeys(...) / click()

The order matters more than beginners expect. findElement searches whatever page is currently loaded in that session, so calling it before get searches the browser’s blank start page and fails with NoSuchElementException. Navigate first, then find.

Both types can search, and that is not an accident. A WebElement can call findElement on itself to search only among its own children. That becomes valuable later for tables, cards, and repeated rows where the same locator would match many places on the page. Today, searching from driver is enough.

One important beginner warning: a WebElement handle can go stale. If the page reloads, or JavaScript replaces that part of the page, the old handle points at a node that no longer exists, and the next method call throws StaleElementReferenceException. Official WebElement documentation notes that method calls check whether the element is still attached to the DOM. Module 4 covers timing and stale elements in depth. For this lesson, follow a simple habit: find the element when you are about to use it, and use it soon after.

A test is more than the browser steps. It also needs a beginning and an end that run every time, whether the test passes or fails. TestNG annotations give you that for free.

Setup prepares a clean browser for the test. In TestNG, @BeforeMethod runs before each @Test method in the class.

@BeforeMethod
public void setUp() {
    driver = new ChromeDriver();
}

Why before each test instead of once for the whole class? So one test cannot leave cookies, an unexpected URL, or an extra tab for the next test. Each test starts from the same known point.

Act is the user story your test performs: open a page, find a field, type into it, click a button. This is where WebDriver and WebElement do their combined work.

Keep the act section short and readable. One test should usually prove one clear behavior. When a test does five unrelated things, a failure only tells you that something in a long chain broke.

Assert answers the question the test exists for: did the application behave correctly? TestNG assertions compare expected and actual values and fail the test when they differ.

WebDriver never decides pass or fail. It drives the browser and reports what it sees. Your test framework makes the judgement. A script with no assertion proves almost nothing.

A good beginner assertion reads a real page value, such as an input’s value after typing, or the page title after navigation.

Teardown ends the session. In TestNG, @AfterMethod runs after each @Test method that ran, including when an assertion failed and the test was marked failed. That reliability is exactly why teardown is the right home for quit().

@AfterMethod
public void tearDown() {
    if (driver != null) {
        driver.quit();
    }
}

The null check protects you when setup itself failed and no driver was ever created, so your teardown does not throw a confusing NullPointerException on top of the original failure.

Calling quit() ends the WebDriver session and closes the windows associated with it, which frees both the browser and the driver process. Skipping teardown is the fastest way to end a study session with five orphan Chrome windows and a slow machine.

Beginners mix these two up constantly. Official WebDriver documentation draws a clear line.

CallMeaning
driver.close()Closes the current window. If it was the last window, the session may end as a side effect.
driver.quit()Ends the driver session and closes associated windows.

For a finished test that opened one browser for one scenario, prefer quit() in teardown. Save close() for the case where you deliberately close one window among several and still need the session alive for the rest. The next lesson practices exactly that with windows and tabs.

Use the Maven project from the previous article (selenium-java-academy, with the selenium-java and TestNG dependencies already added). Create this class under src/test/java/com/codekru/tests/:

package com.codekru.tests;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class WebDriverWebElementLifecycleTest {

    private WebDriver driver;

    @BeforeMethod
    public void setUp() {
        driver = new ChromeDriver();
    }

    @Test
    public void typeIntoLastNameField() {
        driver.get("https://testkru.com/Elements/TextFields");

        WebElement lastName = driver.findElement(By.id("lastNameWithPlaceholder"));
        lastName.clear();
        lastName.sendKeys("codekru");

        Assert.assertEquals(lastName.getAttribute("value"), "codekru");
    }

    @AfterMethod
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }
}
  1. private WebDriver driver; holds the session so setup, test, and teardown methods in the same class instance can all reach it.
  2. @BeforeMethod setUp() creates a fresh Chrome session before the test runs.
  3. driver.get(...) opens the TestKru Text Fields page in that session.
  4. findElement(By.id("lastNameWithPlaceholder")) asks WebDriver for the last name input and stores the returned WebElement.
  5. clear() empties the field first, so a leftover value cannot make the assertion pass or fail for the wrong reason.
  6. sendKeys("codekru") types into that same element.
  7. Assert.assertEquals(...) reads the input’s value attribute and compares it with what you typed.
  8. @AfterMethod tearDown() calls quit() so Chrome does not stay open whether the test passed or failed.

Read the test method again and notice how little Selenium there is. One navigation, one search, two actions, one assertion. That is what a healthy beginner test looks like.

  1. Save the file inside the project from the previous article.
  2. Click the green run icon next to the test method or the class name.
  3. Choose the TestNG runner if IntelliJ asks.
  4. Watch Chrome open, type into the Last Name field, then close.

Chrome opens the Text Fields page, the last name box shows codekru, TestNG reports the test as passed, and Chrome closes because teardown ran.

If findElement throws NoSuchElementException, check two things first. Confirm you opened the exact Text Fields URL, and open the browser inspector to confirm the field still carries id="lastNameWithPlaceholder". Those two checks solve most early failures.

MistakeWhat goes wrongBetter habit
Creating the driver inside the test and never quittingOrphan browsers and locked driver processesAlways pair setup with @AfterMethod and quit()
Using close() for every teardownSession and multi-window cleanup become unclearPrefer quit() when the whole test session is done
Calling findElement before getThe search runs on the wrong page, often a blank start pageNavigate first, then find
Reusing one WebElement after a full page reloadStaleElementReferenceExceptionFind the element again after the page changes
Putting many unrelated clicks in one @TestFailures become hard to diagnoseOne clear behavior per test method
Treating WebDriver as the assertion toolPass and fail stay invisibleUse TestNG Assert to decide the result
  • WebDriver is the Java interface for one browser session: open pages, search the page, and end the session.
  • WebElement is a handle to one HTML control: click, type, read, and check state.
  • findElement is the bridge from WebDriver to WebElement, and you never create a WebElement with new.
  • A reliable test lifecycle runs in a fixed order: setup, act, assert, teardown.
  • Prefer quit() in teardown for a finished session, and save close() for closing one window on purpose.
  • Keep each test focused on one behavior, and let TestNG annotations own setup and cleanup.

You now know the two objects behind Selenium Java browser control and the lifecycle that keeps them safe as your test suite grows.

In the same project, add a second @Test method that:

  1. Uses the same @BeforeMethod and @AfterMethod lifecycle (ChromeDriver plus quit()).
  2. Opens https://testkru.com/Elements/TextFields.
  3. Finds the pre-filled field with By.id("preFilledTextField").
  4. Asserts that getAttribute("value") equals Codekru.
  5. Does not type into that field. Only find it and verify what is already there.

Do not look for a finished solution. Run both tests and confirm that each one opens Chrome, asserts, and closes cleanly.

Continue practicing: Find Element by ID

Next lesson: Browser, Navigation, Window, and Tab Commands. You will practice get, the navigate() commands, window sizing, and moving between tabs and windows while the same WebDriver session stays alive.

Liked the article? Share this on

Leave a Comment

Your email address will not be published. Required fields are marked *