TestNG annotations define when test, setup, cleanup, data-supply, and listener methods run in a Java test suite. In Selenium WebDriver projects, they are commonly used to create a browser session, prepare test data, execute test methods, capture results, and release resources at the correct scope.
This tutorial explains the main TestNG annotations used with Selenium, their execution order, practical differences such as @BeforeTest versus @BeforeMethod, and the annotations used for groups, parameters, data providers, factories, and listeners.
How TestNG Annotations Work with Selenium WebDriver
Selenium WebDriver provides APIs for controlling a browser. TestNG supplies the test lifecycle around those WebDriver operations. For example, a TestNG configuration method can open the browser before a test, while another configuration method closes it after the test finishes.
An annotation is placed directly above a Java method. TestNG discovers the annotation and invokes the method at the appropriate stage of the suite. The annotation does not contain the test logic itself; it declares the role and lifecycle scope of the method.
Prerequisites for a Selenium TestNG Project
- Install a supported Java Development Kit.
- Create a Maven or Gradle Java project.
- Add Selenium Java and TestNG dependencies to the project.
- Install the TestNG integration for the IDE if the IDE requires a separate plugin.
- Configure a compatible browser and WebDriver environment.
- Create
testng.xmlwhen the suite needs explicit classes, groups, parameters, listeners, or parallel execution settings.
Modern Selenium versions can usually resolve supported browser drivers through Selenium Manager. A manually downloaded driver may still be used when the project or execution environment requires explicit driver management.
TestNG Annotation List and Lifecycle Scope
| Annotation | When it runs | Typical Selenium use |
|---|---|---|
@BeforeSuite | Once before the entire suite | Start suite-wide services or reporting |
@AfterSuite | Once after the entire suite | Publish reports or stop shared services |
@BeforeTest | Before the classes inside one XML <test> element | Prepare configuration shared by that XML test |
@AfterTest | After the classes inside one XML <test> element | Clean up resources belonging to that XML test |
@BeforeClass | Once before the first test method in a class | Create a browser shared by methods in one class |
@AfterClass | Once after all test methods in a class | Close a class-level browser |
@BeforeMethod | Before each @Test method | Create a fresh driver or reset test state |
@AfterMethod | After each @Test method | Capture failure evidence and quit the driver |
@BeforeGroups | Before the first method in specified groups | Prepare data for smoke or regression groups |
@AfterGroups | After the last method in specified groups | Remove group-specific test data |
@Test | Runs a test method | Perform browser actions and assertions |
@DataProvider | Supplies multiple data sets to a test | Run the same browser scenario with different inputs |
@Parameters | Receives values from testng.xml | Select a browser, URL, environment, or locale |
@Factory | Creates test-class instances | Build test instances for different configurations |
@Listeners | Registers listener implementations | Add reporting, logging, retry, or screenshot hooks |
TestNG Annotation Execution Order
For a simple suite containing one XML test, one class, and one test method, the configuration order is generally suite, XML test, class, method, test method, and then the matching cleanup methods in reverse scope.
<BeforeSuite>
<BeforeTest>
<BeforeClasses>
<BeforeMethod>
<Test>
</AfterMethod>
</AfterClasses>
</AfterTest>
</AfterSuite>
The conceptual sequence above uses plural class labels, but the actual TestNG annotations are @BeforeClass and @AfterClass. With multiple test methods, the @BeforeMethod and @AfterMethod pair repeats for every method.
@BeforeSuite
@BeforeTest
@BeforeClass
@BeforeMethod
@Test methodOne
@AfterMethod
@BeforeMethod
@Test methodTwo
@AfterMethod
@AfterClass
@AfterTest
@AfterSuite
Do not rely on the alphabetical order of Java method names. Use dependencies when one test genuinely requires another, and avoid making unrelated tests dependent on incidental execution order. Parallel execution can also interleave methods from different classes or XML tests.
Using the @Test Annotation for Selenium Tests
The @Test annotation marks a method as a TestNG test. The method normally contains the browser actions and assertions that verify one behavior. TestNG provides attributes for dependencies, groups, priorities, invocation control, time limits, data providers, and expected exceptions.
@Test attribute | Purpose | Example |
|---|---|---|
groups | Assigns the method to one or more named groups | @Test(groups = {"smoke", "checkout"}) |
dependsOnMethods | Runs the method after named test methods succeed | @Test(dependsOnMethods = "login") |
dependsOnGroups | Makes the method depend on an entire group | @Test(dependsOnGroups = "authentication") |
alwaysRun | Allows a dependent test or configuration method to run despite certain earlier failures | @Test(alwaysRun = true) |
dataProvider | Names the data provider supplying method arguments | @Test(dataProvider = "users") |
dataProviderClass | Identifies a separate class containing the data provider | @Test(dataProvider = "users", dataProviderClass = TestData.class) |
priority | Influences the order of otherwise independent methods | @Test(priority = 1) |
enabled | Includes or excludes the test method | @Test(enabled = false) |
timeOut | Fails a test that exceeds the specified milliseconds | @Test(timeOut = 10000) |
expectedExceptions | Passes only when an expected exception is thrown | @Test(expectedExceptions = IllegalArgumentException.class) |
priority is not a substitute for a real dependency. Use dependsOnMethods or dependsOnGroups when a test must be skipped because a prerequisite failed.
@BeforeMethod and @AfterMethod for Test Isolation
A method annotated with @BeforeMethod runs before every @Test method. A method annotated with @AfterMethod runs after every test method. This scope is suitable when each Selenium test needs a clean browser session and must not inherit cookies, page state, or modified data from another method.
Legacy illustrative code:
@BeforeMethod
public void accountLogin()
{
System.out.println("Account has been logged in")
}
@AfterMethod
public void accountLogout ()
{
System.out.println("Account has been logged out")
}
@test(priority=0)
public void updateProfile()
{
System.out.println("Profile has been updated using the updateProfile method")
}
@test(priority=1)
public void bankBlance()
{
System.out.println("Bank balance will be shown using the bankBlance method" )
}
The snippet demonstrates the intended lifecycle: login setup, one test method, and logout cleanup. In compilable Java, annotation names are case-sensitive, so the test annotation must be written as @Test. Java statements such as System.out.println(...) also require semicolons.
First test-method sequence:
"Account has been logged in."
"Profile has been updated using the updateProfile method."
"Account has been logged out."
Second test-method sequence:
"Account has been logged in."
"Bank balance will be shown using the bank balance method."
"Account has been logged out."
Capturing a Selenium Screenshot in @AfterMethod
TestNG can inject ITestResult into an @AfterMethod method. This makes it possible to capture a screenshot only when the associated test fails. Using alwaysRun = true helps ensure that cleanup is attempted even when a setup or test step fails.
private WebDriver driver;
@BeforeMethod
public void startBrowser() {
driver = new ChromeDriver();
}
@AfterMethod(alwaysRun = true)
public void stopBrowser(ITestResult result) throws IOException {
if (driver != null && !result.isSuccess()) {
byte[] image = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.BYTES);
Files.write(Path.of("failure-" + result.getMethod().getMethodName() + ".png"), image);
}
if (driver != null) {
driver.quit();
}
}
@BeforeClass and @AfterClass for a Shared Class-Level Browser
@BeforeClass runs once before the first test method in the current class. @AfterClass runs once after all test methods in that class. This scope can reduce browser startup time, but every method then shares the same browser session and application state.
A shared class-level driver is appropriate only when the tests deliberately manage state between methods. For independent tests, method-level setup and cleanup usually provide clearer failure boundaries.
Legacy illustrative code:
@BeforeClass
public void accountLogin()
{
System.out.println("Account has been logged in")
}
@AfterClass
public void accountLogout()
{
System.out.println("Account has been logged out")
}
@Test(priority=0)
public void updateProfile()
{
System.out.println("Profile has been updated using updateProfile method")
}
@Test(priority=1)
public void bankBlance()
{
System.out.println("Bank balance will be shown using the bankBlance method" )
}
The intended output places the class setup before both tests and the class cleanup after both tests:
"Account has been logged in."
"Profile has been updated using the updateProfile method"
"Bank balance will be shown using the bankBlance method"
"Account has been logged out."
@BeforeTest and @AfterTest in testng.xml
In TestNG, the word test has two relevant meanings. A Java method marked with @Test is a test method. By contrast, @BeforeTest and @AfterTest are scoped to the classes contained in an XML <test> element.
@BeforeTest runs before any test method belonging to those classes. @AfterTest runs after their test methods have completed. They do not run before and after every Java method; use @BeforeMethod and @AfterMethod for that behavior.
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Browser suite">
<test name="Chrome checkout tests">
<parameter name="browser" value="chrome"/>
<classes>
<class name="tests.CartTest"/>
<class name="tests.PaymentTest"/>
</classes>
</test>
</suite>
In this configuration, one @BeforeTest method can prepare resources for both CartTest and PaymentTest. The corresponding @AfterTest method runs after both classes finish.
Legacy illustrative code:
public class Testngfile {
public String basePath = "https://mindmajix.com/selenium-training";
String path = "D:\\svcdriver.exe";
public WebDriver automation;
@BeforeTest
public void openBrowser() {
System.out.println("opening chrome browser");
System.setProperty("webdriver.firefox.nicole", path);
automation = new FirefoxDriver();
automation.get(basePath);
}
@Test
public void welcomePage() {
String welcomeMessage = "Hello Connections";
String requiredMessage =automation.getTitle();
Assert.assertEquals(requiredMessage, welcomeMessage);
}
@AfterTest
public void discontinueBrowser(){
automation.close();
}
}
This older example shows the lifecycle concept but contains inconsistent browser and driver-property names. In current Selenium code, prefer a correctly configured driver and call quit() when the entire browser session should be terminated.
@BeforeSuite and @AfterSuite for Suite-Wide Resources
@BeforeSuite and @AfterSuite run once for the entire TestNG suite. They are suitable for suite-wide reporting, temporary services, shared test-data preparation, or other resources that genuinely belong to every XML test.
A single WebDriver instance should not normally be shared across an entire parallel suite. WebDriver instances are not designed to be used concurrently by multiple test threads. Create a separate driver per test method, class, XML test, or thread according to the suite design.
Legacy illustrative code:
public class SuiteInitialization() {
@BeforeSuite(alwaysRun = true)
public void initializationSuite() {
WebDriver automation = new FirefoxDriver();
}
@AfterSuite(alwaysRun = true)
public void discontinue() {
automation().close();
}
}
The preceding snippet illustrates suite-level intent rather than a complete compilable class. A real implementation must keep the driver in an accessible field and ensure that cleanup handles partial initialization safely.
@BeforeGroups and @AfterGroups for Selenium Test Groups
Group configuration annotations run around specified TestNG groups. They are useful when only a subset of tests needs particular data or configuration. For example, checkout tests may require a prepared shopping cart, while unrelated account tests do not.
@BeforeGroups("checkout")
public void prepareCheckoutData() {
System.out.println("Preparing checkout data");
}
@Test(groups = "checkout")
public void placeOrder() {
System.out.println("Placing an order");
}
@AfterGroups("checkout")
public void removeCheckoutData() {
System.out.println("Removing checkout data");
}
The before-group method runs before the first test method belonging to checkout, and the after-group method runs after the final method in that group.
Data-Driven Selenium Tests with @DataProvider
A method annotated with @DataProvider supplies sets of arguments to an associated test method. TestNG invokes the test once for each returned data row. This is useful for checking multiple valid inputs, roles, locales, or browser configurations without duplicating test logic.
@DataProvider(name = "searchTerms")
public Object[][] searchTerms() {
return new Object[][] {
{"TestNG annotations"},
{"Selenium WebDriver"}
};
}
@Test(dataProvider = "searchTerms")
public void searchDisplaysResults(String term) {
driver.get("https://example.com/search");
driver.findElement(By.name("q")).sendKeys(term);
driver.findElement(By.cssSelector("button[type='submit']")).click();
Assert.assertFalse(
driver.findElements(By.cssSelector(".search-result")).isEmpty(),
"Expected at least one result for: " + term
);
}
If the provider is located in another class, set both dataProvider and dataProviderClass on @Test. Data providers can execute in parallel, but each parallel invocation must receive isolated WebDriver and test-data state.
Passing Selenium Configuration with @Parameters and @Optional
@Parameters maps values from testng.xml to Java method arguments. @Optional provides a fallback when the XML parameter is absent. Parameters are commonly used for browser names, base URLs, environments, and other suite configuration values.
@Parameters({"browser", "baseUrl"})
@BeforeClass
public void configureBrowser(
@Optional("chrome") String browser,
@Optional("https://example.com") String baseUrl) {
if (browser.equalsIgnoreCase("firefox")) {
driver = new FirefoxDriver();
} else {
driver = new ChromeDriver();
}
driver.get(baseUrl);
}
Use XML parameters for suite configuration and a data provider for multiple test-data rows. They solve different problems even though both can pass values into annotated methods.
What @Factory Does in TestNG
@Factory creates instances of a TestNG test class. Each instance can hold a different constructor value, allowing the same set of test methods to run against multiple configurations. Unlike @DataProvider on a test method, a factory parameterizes the test-class instance.
public class BrowserTestFactory {
@Factory
public Object[] createBrowserTests() {
return new Object[] {
new NavigationTest("chrome"),
new NavigationTest("firefox")
};
}
}
public class NavigationTest {
private final String browser;
public NavigationTest(String browser) {
this.browser = browser;
}
@Test
public void opensHomePage() {
System.out.println("Running navigation test in " + browser);
}
}
A factory can also receive its instance data from a data provider. For large cross-browser suites, keep driver creation and thread isolation separate from the factory so that each instance owns the correct resources.
Complete Selenium TestNG Annotation Example
The following example uses method-level setup to give each test an independent Chrome session. The cleanup method receives the test result, records failure information, and always quits the browser when initialization succeeded.
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class LoginTest {
private WebDriver driver;
@BeforeMethod
public void openBrowser() {
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
driver.get("https://example.com/login");
}
@Test(groups = "smoke")
public void validUserCanOpenAccountPage() {
driver.findElement(By.id("username")).sendKeys("demo-user");
driver.findElement(By.id("password")).sendKeys("demo-password");
driver.findElement(By.cssSelector("button[type='submit']")).click();
Assert.assertTrue(
driver.getCurrentUrl().contains("/account"),
"The account page was not opened"
);
}
@AfterMethod(alwaysRun = true)
public void closeBrowser(ITestResult result) {
if (!result.isSuccess()) {
System.out.println("Failed test: " + result.getName());
}
if (driver != null) {
driver.quit();
}
}
}
The example URL and locators are placeholders. Replace them with the application URL, stable locators, test accounts, and credential-handling approach used by the project. Do not store production credentials directly in test source code.
Choosing the Correct TestNG Annotation Scope
- Use
@BeforeMethodand@AfterMethodwhen every test requires isolated browser state. - Use
@BeforeClassand@AfterClasswhen methods in one class intentionally share an expensive resource or browser session. - Use
@BeforeTestand@AfterTestfor resources shared by the classes in one XML<test>. - Use
@BeforeSuiteand@AfterSuiteonly for resources that belong to the whole suite. - Use group annotations when setup applies only to specifically named groups.
- Use
@DataProviderfor multiple input rows and@Parametersfor XML-driven configuration. - Use
@Factorywhen each test-class instance needs its own constructor-based configuration.
Common TestNG Annotation Mistakes in Selenium
- Confusing @BeforeTest with @BeforeMethod:
@BeforeTestrefers to an XML test, not to each@Testmethod. - Calling close() instead of quit():
close()closes the current window, whilequit()terminates the WebDriver session and all associated windows. - Sharing one driver across parallel tests: concurrent tests can overwrite browser state and produce intermittent failures.
- Omitting alwaysRun from critical cleanup: cleanup may not run after some failed prerequisites unless it is configured appropriately.
- Using priority as a dependency: priority influences ordering but does not express the pass-or-skip relationship provided by dependency attributes.
- Ignoring null-safe cleanup: browser construction can fail, so teardown should verify that the driver was initialized.
- Depending on method-name order: method names should not be used as an implicit test workflow.
TestNG Annotations in Selenium FAQs
What are the main TestNG annotations used in Selenium?
The primary lifecycle annotations are @BeforeSuite, @BeforeTest, @BeforeClass, @BeforeMethod, @Test, @AfterMethod, @AfterClass, @AfterTest, and @AfterSuite. TestNG also provides group configuration, data-provider, parameter, factory, and listener annotations.
What is the difference between @BeforeTest and @BeforeMethod?
@BeforeTest runs before the test methods belonging to the classes inside an XML <test>. @BeforeMethod runs before each individual Java method annotated with @Test.
What does @AfterTest do in TestNG?
@AfterTest runs after all test methods associated with a particular XML <test> element have finished. It is used to release resources shared within that XML test scope.
What is @Factory in TestNG?
@Factory marks a method or constructor that produces test-class instances. It is useful when the entire test class, rather than only one test method, must run with multiple constructor-supplied configurations.
Which TestNG annotation should open and close a browser?
Use @BeforeMethod and @AfterMethod when every test needs a fresh browser. Use class-level or XML-test-level annotations only when browser sharing is intentional and state is carefully controlled.
Editorial QA Checklist for This TestNG Annotation Tutorial
- Confirm that
@BeforeTestand@AfterTestare described as XML<test>-scoped annotations. - Confirm that the lifecycle uses the singular annotation names
@BeforeClassand@AfterClass. - Check that every new Java example uses
language-javaand every new XML example useslanguage-xml. - Verify that Selenium cleanup examples guard against a null driver and call
quit()when ending the complete session. - Check that parallel-execution guidance does not suggest sharing one WebDriver instance across threads.
- Confirm that
@DataProvider,@Parameters, and@Factoryare distinguished by their actual roles. - Validate browser options, imports, locators, and dependency versions against the versions used by the project before executing the examples.
For the complete annotation definitions and supported attributes, refer to the official TestNG annotations documentation.
TestNG Annotation Summary for Selenium Projects
The correct annotation depends on the lifetime of the resource being managed. Method-level annotations provide strong test isolation, class-level annotations share state within one class, XML-test annotations cover the classes in one <test>, and suite annotations cover the complete run. Group, data-provider, parameter, factory, and listener annotations handle more specialized test organization and execution requirements.
TutorialKart.com