---
title: TestNG annotations and the driver lifecycle | TestingBot
description: TestNG has ten configuration annotations, and which one you use to create
  and quit the browser decides how isolated, how fast and how parallel-safe your…
source_url:
  html: https://testingbot.com/resources/courses/selenium-testng/annotations
  md: https://testingbot.com/resources/courses/selenium-testng/annotations.md
---

Course

[Selenium Java Tutorial with TestNG](https://testingbot.com/resources/courses/selenium-testng)

Chapter 4 of 11

1. [1 Selenium and TestNG: how they fit together](https://testingbot.com/resources/courses/selenium-testng)
2. [2 Set up a Maven project for Selenium](https://testingbot.com/resources/courses/selenium-testng/project-setup)
3. [3 Your first Selenium test with TestNG](https://testingbot.com/resources/courses/selenium-testng/first-test)
4. [4 TestNG annotations and the driver lifecycle](https://testingbot.com/resources/courses/selenium-testng/annotations)
5. [5 Hard assertions vs SoftAssert](https://testingbot.com/resources/courses/selenium-testng/assertions)
6. [6 Data-driven tests with DataProvider](https://testingbot.com/resources/courses/selenium-testng/data-providers)
7. [7 Suites, groups and parameters in testng.xml](https://testingbot.com/resources/courses/selenium-testng/testng-xml)
8. [8 Run Selenium tests in parallel](https://testingbot.com/resources/courses/selenium-testng/parallel-execution)
9. [9 Listeners, screenshots and reports](https://testingbot.com/resources/courses/selenium-testng/listeners-and-reports)
10. [10 Run the suite on a Selenium grid](https://testingbot.com/resources/courses/selenium-testng/run-on-a-selenium-grid)
11. [11 Knowledge check](https://testingbot.com/resources/courses/selenium-testng/knowledge-check)

[Exit course](https://testingbot.com/resources/courses)

 Chapter 4 of 11 Intermediate 7 min read 3 quick checks Updated Nov 2026 

# TestNG annotations and the driver lifecycle
On this page
1. [The execution order](https://testingbot.com#the-execution-order)
2. [Where should the browser be created?](https://testingbot.com#where-should-the-browser-be-created)
3. [alwaysRun and groups](https://testingbot.com#alwaysrun-and-groups)
4. [Moving the lifecycle into a base class](https://testingbot.com#moving-the-lifecycle-into-a-base-class)
5. [Injecting context into configuration methods](https://testingbot.com#injecting-context-into-configuration-methods)
6. [@Test attributes worth knowing](https://testingbot.com#test-attributes-worth-knowing)

TestNG has ten configuration annotations, and which one you use to create and quit the browser decides how isolated, how fast and how parallel-safe your suite is. This chapter maps them out, then moves the driver lifecycle into a base class.

## The execution order[#](https://testingbot.com#the-execution-order)

For a suite with one `<test>` containing one class with two test methods, TestNG runs:

```
@BeforeSuite once per suite
  @BeforeTest once per <test> block in testng.xml
    @BeforeClass once per test class
      @BeforeMethod before each @Test method
        @Test testA
      @AfterMethod
      @BeforeMethod
        @Test testB
      @AfterMethod
    @AfterClass
  @AfterTest
@AfterSuite
```

There are also `@BeforeGroups` and `@AfterGroups`, which run before the first and after the last test method of a named group.

Two details matter more than the diagram suggests:

- **`@BeforeTest` is about the `<test>` tag in `testng.xml`, not about test methods.** People regularly put driver setup in `@BeforeTest` expecting it to run per test method, then find that every method in the `<test>` block shares one browser.
- **Inheritance order.** `@Before...` methods in a superclass run before those in a subclass; `@After...` methods run in reverse (subclass first, then superclass). That is what makes a base class safe: the browser exists before any subclass setup runs and is still alive during subclass teardown.

You can see the order yourself with a throwaway class:

```java
package com.example;

import org.testng.annotations.*;

public class LifecycleDemoTest {
    @BeforeSuite public void beforeSuite() { System.out.println("BeforeSuite"); }
    @BeforeTest public void beforeTest() { System.out.println(" BeforeTest"); }
    @BeforeClass public void beforeClass() { System.out.println(" BeforeClass"); }
    @BeforeMethod public void beforeMethod() { System.out.println(" BeforeMethod"); }

    @Test public void testA() { System.out.println(" testA"); }
    @Test public void testB() { System.out.println(" testB"); }

    @AfterMethod public void afterMethod() { System.out.println(" AfterMethod"); }
    @AfterClass public void afterClass() { System.out.println(" AfterClass"); }
    @AfterTest public void afterTest() { System.out.println(" AfterTest"); }
    @AfterSuite public void afterSuite() { System.out.println("AfterSuite"); }
}
```

Run it with `mvn test -Dtest=LifecycleDemoTest` and compare the console output with the diagram. Note that TestNG does not promise an order between `testA` and `testB`; don't write tests that rely on one.

## Where should the browser be created?[#](https://testingbot.com#where-should-the-browser-be-created)

| Create in | One browser per | Isolation | Speed | Parallel-safe |
| --- | --- | --- | --- | --- |
| `@BeforeSuite` | whole run | none: cookies, local storage and open dialogs leak between all tests | fastest | no |
| `@BeforeClass` | test class | tests in a class affect each other | fast | only with `parallel="classes"` |
| `@BeforeMethod` | test method | full | slowest (a new session per test) | yes, with `ThreadLocal` (chapter 7) |

For UI tests, **create the driver in `@BeforeMethod` and quit it in `@AfterMethod`**. Starting a new session for every test costs time, more so on a remote grid, but in return every test starts clean, can run in any order, can run in parallel, and fails for its own reasons rather than because the previous test left a modal open. When a per-class browser does make sense (a long read-only walk through one page, say), use `@BeforeClass` deliberately and keep that class out of method-level parallelism.

Use `@BeforeSuite` and `@BeforeClass` for things that are expensive and safe to share: reading configuration, creating test data through an API, starting a local mock server.

One class walks through a long read-only report page with twelve checks and never changes state. When is a per-class browser in `@BeforeClass` reasonable?

 Never; every class must use `@BeforeSuite` Always, because it is faster for every class Here, chosen deliberately, with that class kept out of method-level parallelism Only if the driver is static

A shared browser is acceptable when tests cannot affect each other, but its methods must not run in parallel. Making the driver static would share it across classes too.

## alwaysRun and groups[#](https://testingbot.com#alwaysrun-and-groups)

Configuration methods belong to groups too. If you run only the `smoke` group (chapter 6) and your `@BeforeMethod` has no group, TestNG skips it, and every smoke test fails with a `NullPointerException` on `driver`. Mark lifecycle methods with `alwaysRun = true`:

- On a `@Before...` method it means "run regardless of which groups are selected".
- On an `@After...` method it additionally means "run even if a previous configuration or test method failed", which is what guarantees `quit()` is called.

## Moving the lifecycle into a base class[#](https://testingbot.com#moving-the-lifecycle-into-a-base-class)

Create `BaseTest.java`:

```java
package com.example;

import java.time.Duration;
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;

public abstract class BaseTest {

    private WebDriver driver;

    protected WebDriver driver() {
        return driver;
    }

    protected WebDriverWait waitUpTo(int seconds) {
        return new WebDriverWait(driver, Duration.ofSeconds(seconds));
    }

    @BeforeMethod(alwaysRun = true)
    public void startDriver() {
        ChromeOptions options = new ChromeOptions();
        if (Boolean.getBoolean("headless")) {
            options.addArguments("--headless=new", "--window-size=1920,1080");
        }
        driver = new ChromeDriver(options);
    }

    @AfterMethod(alwaysRun = true)
    public void stopDriver() {
        if (driver != null) {
            driver.quit();
            driver = null;
        }
    }
}
```

and slim the test down:

```java
package com.example;

import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.testng.Assert;
import org.testng.annotations.Test;

public class FirstTest extends BaseTest {

    @Test
    public void submitsTheWebForm() {
        driver().get("https://www.selenium.dev/selenium/web/web-form.html");
        driver().findElement(By.id("my-text-id")).sendKeys("Selenium");
        driver().findElement(By.cssSelector("button[type='submit']")).click();

        String message = waitUpTo(10)
                .until(ExpectedConditions.visibilityOfElementLocated(By.id("message")))
                .getText();
        Assert.assertEquals(message, "Received!");
    }
}
```

`Boolean.getBoolean("headless")` reads a JVM system property, so `mvn test -Dheadless=true` switches to headless without a code change.

Why an accessor method `driver()` rather than a `protected WebDriver driver` field? Because in chapter 7 the driver moves into a `ThreadLocal` for parallel runs. With every test calling `driver()`, that change touches one file. With a public field, it touches every test.

Why does `BaseTest` expose the driver through a `driver()` method rather than a `protected` field?

 Fields cannot hold a `WebDriver` So moving the driver into a `ThreadLocal` later changes one file instead of every test Methods are faster than fields TestNG requires accessor methods

Every test calls `driver()`, so the storage behind it can change for parallel runs without touching any test class.

## Injecting context into configuration methods[#](https://testingbot.com#injecting-context-into-configuration-methods)

TestNG can pass useful objects into configuration methods if you declare them as parameters. Two you will use:

```java
import java.lang.reflect.Method;
import org.testng.ITestResult;

@BeforeMethod(alwaysRun = true)
public void startDriver(Method method) {
    System.out.println("Starting browser for " + method.getName());
    // ...
}

@AfterMethod(alwaysRun = true)
public void stopDriver(ITestResult result) {
    System.out.println(result.getName() + " passed? " + result.isSuccess());
    // ...
}
```

`Method` gives you the test method about to run (useful for naming the session on a grid). `ITestResult` tells you how it ended (useful for reporting pass or fail to a grid). Chapter 9 uses both.

## @Test attributes worth knowing[#](https://testingbot.com#test-attributes-worth-knowing)

```java
@Test(description = "Form submission shows a confirmation",
      groups = {"smoke"},
      timeOut = 60_000, // fail if the method runs longer than 60s
      enabled = true) // false to switch a test off
public void submitsTheWebForm() { ... }
```

`timeOut` is a client-side safety net. Remote grids usually enforce their own limit as well; on TestingBot that is the [maximum test duration option](https://testingbot.com/support/web-automate/selenium/test-options#maxduration), which stops a hung session from running on.

`priority` also exists and orders methods within a class (lower runs first), but needing it is usually a sign that tests depend on each other. `dependsOnMethods` (chapter 6) states that dependency explicitly, and tests that need neither are the ones that parallelise cleanly.

The [TestNG with Selenium article](https://testingbot.com/resources/articles/testng-selenium) has a compact reference of every annotation if you want one page to bookmark.

Your tests only pass when you give them `priority = 1`, `2` and `3`. What does that tell you?

 The tests depend on each other; make them independent or state the dependency with `dependsOnMethods` Priority is required for every test The thread count is too low

Needing an order means one test relies on another's leftovers. Independent tests run in any order and in parallel.

[Previous · Chapter 3 Your first Selenium test with TestNG](https://testingbot.com/resources/courses/selenium-testng/first-test) [Next · Chapter 5 Hard assertions vs SoftAssert](https://testingbot.com/resources/courses/selenium-testng/assertions)

On this page

1. [The execution order](https://testingbot.com#the-execution-order)
2. [Where should the browser be created?](https://testingbot.com#where-should-the-browser-be-created)
3. [alwaysRun and groups](https://testingbot.com#alwaysrun-and-groups)
4. [Moving the lifecycle into a base class](https://testingbot.com#moving-the-lifecycle-into-a-base-class)
5. [Injecting context into configuration methods](https://testingbot.com#injecting-context-into-configuration-methods)
6. [@Test attributes worth knowing](https://testingbot.com#test-attributes-worth-knowing)

Certification

This course has a free, hands-on certificate.

[How it works](https://testingbot.com/resources/courses/selenium-testng/certification)
