A Guide to TestNG: Building Robust Testing Frameworks

Samiksha KuteSamiksha Kute
12 min read

Welcome to this comprehensive guide on TestNG, a powerful testing framework widely used in Java-based automation projects. Whether you're new to automation testing or looking to enhance your framework-building skills, this guide will walk you through TestNG’s core features, setup, and practical applications. By the end, you’ll have a clear understanding of how TestNG can help you control test execution, manage test cases, and build robust automation frameworks.

What is TestNG?

TestNG(Test Next Generation) is a testing framework designed to simplify and enhance the process of writing and executing test cases, particularly for Java-based automation projects like Selenium, Appium, and REST API testing. It provides greater control over test case execution compared to plain Java, offering features like annotations, parameterization, parallel execution, and detailed reporting. TestNG is a cornerstone for building automation frameworks because it allows you to organize, prioritize, and execute test cases efficiently.

Why Use TestNG?

  • Control Over Test Execution: You can define dependencies, group test cases, and control the order of execution.

  • Flexibility: Run specific test cases (e.g., smoke tests) or skip others with a single click.

  • Data-Driven Testing: Test cases can be run with multiple sets of data using parameterization.

  • Parallel Execution: Execute tests simultaneously to save time.

  • Reporting: Generate detailed reports to track test results.

  • Annotations: Simplify test setup, execution, and cleanup with predefined annotations.

In this guide, we’ll explore how to install TestNG, create test cases, use annotations, configure XML files, and leverage advanced features like listeners and parameterization.

Installing and Configuring TestNG in Eclipse

Let’s start by setting up TestNG in Eclipse, a popular IDE for Java development.

  1. Visit the Official Website:

    • Go to testng.org to learn more about TestNG. The homepage describes it as a testing framework with features like annotations, data-driven testing, and flexible test execution.
  2. Install the TestNG Plugin in Eclipse:

    • Open Eclipse and navigate to Help > Eclipse Marketplace.

    • In the search bar, type “TestNG” and press Enter.

    • Find TestNG for Eclipse, click Install, and follow the prompts (click Next, accept the agreement, and click Finish).

    • Eclipse will install the plugin in the background. Once complete (100%), you may see a security warning; click Install Anyway.

    • Restart Eclipse to activate the TestNG plugin.

  3. Verify Installation:

    • After restarting, go to Window > Preferences.

    • In the search bar, type “TestNG”. If you see a TestNG option, the plugin is successfully installed. If not, repeat the installation process.

Creating a TestNG Project

Now that TestNG is installed, let’s create a new project to explore its features.

  1. Create a Java Project:

    • In Eclipse, right-click in the Package Explorer and select New > Java Project.

    • Name the project (e.g., “TestNGTutorial”) and click Finish.

  2. Create a Package:

    • Right-click on the project, select New > Package, and name it (e.g., “test”).

    • Packages help organize your test classes.

  3. Create a Test Class:

    • Right-click on the package, select New > Class, and name it (e.g., “Day1”).

    • By default, Eclipse includes the public static void main method. In TestNG, you don’t need this because TestNG acts as its own compiler.

  4. Write a Simple Test:

    • Remove the public static void main method.

    • Create a method, e.g., public void demo().

    • Add a simple statement like System.out.println("hello");.

    • Add the @Test annotation above the method:

        import org.testng.annotations.Test;
      
        public class Test1 {
            @Test
            public void demo() {
                System.out.println("hello");
            }
        }
      
    • The @Test annotation tells TestNG that this method is a test case.

  5. Run the Test:

    • Right-click the class, select Run As > TestNG Test.

    • The output will show “hello” along with a neat TestNG report indicating that one test passed.

Key Difference from Java

In plain Java, you need the public static void main method to run a program. Without it, the code won’t execute. TestNG eliminates this dependency by using its own compiler and annotations like @Test, making test execution simpler and more organized.

Understanding TestNG XML Files

The TestNG XML file is the heart of a TestNG framework. It allows you to configure and control test execution, making it easier to manage large numbers of test cases.

Creating a TestNG XML File

  1. Right-click on the project and select TestNG > Convert to TestNG.

  2. Click Next and Finish to generate a testng.xml file.

The XML file has a hierarchical structure:

  • Suite: The top-level container (e.g., “Loan Department”).

  • Test: A module or folder within the suite (e.g., “Personal Loan”).

  • Classes: Java class files containing test cases.

  • Methods: Individual test methods within a class.

Example testng.xml:

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Loan Department">
    <test name="Personal Loan">
        <classes>
            <class name="test.Test1"/>
            <class name="test.Test2"/>
        </classes>
    </test>
</suite>

Running Tests via XML

  • Right-click the testng.xml file and select Run As > TestNG Suite.

  • All test cases defined in the XML will execute, and TestNG will generate a detailed report.

Benefits of XML Files

  • Modular Execution: Run specific modules (e.g., only “Personal Loan” test cases) by modifying the XML.

  • Scalability: Easily manage hundreds of test cases by adding them to the XML.

  • Single-Click Execution: Trigger all relevant test cases with one command.

Controlling Test Execution

TestNG provides powerful mechanisms to control which test cases run and in what order.

Grouping Test Cases

You can group test cases into categories (e.g., “smoke”, “regression”) and run only specific groups. For example, if you have 500 test cases and want to run only 40 smoke tests, TestNG can filter and execute them with a single click.

Including and Excluding Test Cases

You can include or exclude specific test methods or modules using the include and exclude tags in the XML file.

Example: Excluding a specific test method:

<test name="Car Loan">
    <classes>
        <class name="test.Test3">
            <methods>
                <exclude name="mobileLoginCar"/>
            </methods>
        </class>
    </classes>
</test>
  • This skips the mobileLoginCar test method while running others in the Test3 class.

Example: Including a specific test method:

<test name="Personal Loan">
    <classes>
        <class name="test.Test1">
            <methods>
                <include name="demo"/>
            </methods>
        </class>
    </classes>
</test>
  • This runs only the demo test method, excluding others in the Test1 class.

Using Regular Expressions

For large projects with many test cases, manually including or excluding each test is impractical. TestNG supports regular expressions to filter test methods based on naming conventions.

Example: Exclude all mobile-related test cases:

<test name="Car Loan">
    <classes>
        <class name="test.Test3">
            <methods>
                <exclude name="mobile.*"/>
            </methods>
        </class>
    </classes>
</test>
  • The mobile.* pattern excludes any method starting with “mobile” (e.g., mobileLoginCar, mobileSignInCar).

Tip: Follow a consistent naming convention in your framework (e.g., start all mobile test methods with “mobile”) to make filtering easier.

Running Tests by Package

You can run all test cases in a specific package without listing individual classes:

<suite name="Loan Department">
    <test name="All Tests">
        <packages>
            <package name="test"/>
        </packages>
    </test>
</suite>
  • This runs all test cases in the “test” package, ideal for regression testing.

TestNG Annotations

Annotations are a core feature of TestNG, allowing you to define the behavior of test methods and set up prerequisites or cleanup tasks.

Common Annotations

  1. @Test: Marks a method as a test case.

     @Test
     public void demo() {
         System.out.println("hello");
     }
    
  2. @BeforeTest: Runs before all test cases in a test folder (e.g., clean the database).

     @BeforeTest
     public void setup() {
         System.out.println("I will execute first");
     }
    
  3. @AfterTest: Runs after all test cases in a test folder (e.g., close connections).

     @AfterTest
     public void cleanup() {
         System.out.println("I will execute last");
     }
    
  4. @BeforeMethod: Runs before each test method (e.g., initialize a browser).

  5. @AfterMethod: Runs after each test method (e.g., close the browser).

Helper Attributes for @Test

  • dependsOnMethods: Specifies that a test method depends on others.

      @Test(dependsOnMethods = {"webLoginCar", "mobileLoginCar"})
      public void apiLoginCar() {
          System.out.println("API login car");
      }
    
    • apiLoginCar runs only after webLoginCar and mobileLoginCar pass.
  • enabled: Skips a test method if set to false.

      @Test(enabled = false)
      public void mobileSignInCar() {
          System.out.println("Mobile sign in car");
      }
    
    • This skips the mobileSignInCar test without commenting it out.
  • timeOut: Sets a timeout for a test method (in milliseconds).

      @Test(timeOut = 4000)
      public void slowTest() {
          // Test fails if it takes longer than 4 seconds
      }
    
    • Useful for slow APIs or UI interactions.

Parameterization in TestNG

Parameterization allows you to run test cases with different data sets, reducing code duplication.

1. XML-Based Parameterization

You can define global or test-specific parameters in the testng.xml file.

Example: Global parameter (applies to all test folders):

<suite name="Loan Department">
    <parameter name="url" value="google.com"/>
    <test name="Personal Loan">
        <classes>
            <class name="test.Test1"/>
        </classes>
    </test>
</suite>

Example: Test-specific parameter:

<test name="Car Loan">
    <parameter name="url" value="carloan.com"/>
    <classes>
        <class name="test.Test3"/>
    </classes>
</test>

Accessing parameters in a test:

@Test
@Parameters("url")
public void webLoginCar(String urlName) {
    System.out.println(urlName); // Prints "carloan.com"
}

You can pass multiple parameters:

<test name="Car Loan">
    <parameter name="url" value="carloan.com"/>
    <parameter name="username" value="123456"/>
    <classes>
        <class name="test.Test3"/>
    </classes>
</test>
@Test
@Parameters({"url", "username"})
public void webLoginCar(String urlName, String key) {
    System.out.println(urlName); // Prints "carloan.com"
    System.out.println(key);     // Prints "123456"
}

2. Data Provider Annotation

The @DataProvider annotation allows you to run a test method with multiple data sets without relying on the XML file.

Example: Testing a login feature with three sets of credentials:

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class Test3 {
    @DataProvider(name = "getData")
    public Object[][] getData() {
        Object[][] data = new Object[3][2]; // 3 rows, 2 columns
        // First set
        data[0][0] = "firstSetUsername";
        data[0][1] = "firstPassword";
        // Second set
        data[1][0] = "secondSetUsername";
        data[1][1] = "secondPassword";
        // Third set
        data[2][0] = "thirdSetUsername";
        data[2][1] = "thirdPassword";
        return data;
    }

    @Test(dataProvider = "getData")
    public void mobileSignOutCar(String username, String password) {
        System.out.println(username + ", " + password);
    }
}
  • The getData method returns a 2D array with three rows (three test runs) and two columns (username and password).

  • The mobileSignOutCar test runs three times, each with a different username and password.

  • Output:

      firstSetUsername, firstPassword
      secondSetUsername, secondPassword
      thirdSetUsername, thirdPassword
    

Tip: Ensure the number of parameters in the test method matches the number of columns in the data provider array.

TestNG Listeners

Listeners in TestNG monitor test execution and perform actions based on test outcomes (e.g., success, failure). They are useful for tasks like taking screenshots on test failure or logging detailed responses.

Creating a Listener

  1. Create a new class (e.g., Listeners) that implements the ITestListener interface:

     import org.testng.ITestListener;
     import org.testng.ITestResult;
    
     public class Listeners implements ITestListener {
         @Override
         public void onTestSuccess(ITestResult result) {
             System.out.println("I successfully executed: " + result.getName());
         }
    
         @Override
         public void onTestFailure(ITestResult result) {
             System.out.println("I failed executing: " + result.getName());
             // Add screenshot code here for Selenium/Appium
         }
    
         @Override
         public void onTestStart(ITestResult result) {}
         @Override
         public void onStart(ITestContext context) {}
         @Override
         public void onFinish(ITestContext context) {}
         @Override
         public void onTestSkipped(ITestResult result) {}
         @Override
         public void onTestFailedButWithinSuccessPercentage(ITestResult result) {}
     }
    
  2. Configure the listener in the testng.xml file:

     <suite name="Loan Department">
         <listeners>
             <listener class-name="testng.Listeners"/>
         </listeners>
         <test name="Personal Loan">
             <classes>
                 <class name="testng.Test1"/>
             </classes>
         </test>
     </suite>
    

Use Cases

  • On Failure: Capture screenshots for Selenium/Appium tests or log API responses.

  • On Success: Log success messages or update custom reports.

  • Tracking Failures: Use result.getName() to identify the failed test method.

Example Output (if demo test fails):

hello
I failed executing: demo

Running Tests in Parallel

TestNG allows you to run tests in parallel to reduce execution time, though this should be used cautiously to avoid performance issues.

Parallel Execution at Suite Level

To run test folders (e.g., “Personal Loan” and “Car Loan”) in parallel:

<suite name="Loan Department" parallel="tests" thread-count="2">
    <test name="Personal Loan">
        <classes>
            <class name="test.Test1"/>
        </classes>
    </test>
    <test name="Car Loan">
        <classes>
            <class name="test.Test3"/>
        </classes>
    </test>
</suite>
  • parallel="tests": Runs test folders in parallel.

  • thread-count="2": Allows two tests to run simultaneously.

Parallel Execution at Class Level

To run classes within a test folder in parallel:

<test name="Personal Loan" parallel="classes" thread-count="2">
    <classes>
        <class name="test.Test1"/>
        <class name="test.Test2"/>
    </classes>
</test>

Note: Parallel execution is more effective with real automation code (e.g., browser-based tests). For mobile testing (Appium), you need multiple devices/emulators. For APIs, parallel execution is feasible but may stress the server.

TestNG Reporting

After running tests, TestNG generates a report in the test-output folder:

  1. Refresh your project in Eclipse.

  2. Open the test-output folder and locate index.html.

  3. Copy the file path, paste it into a browser, and view the report.

    A test results interface for a loan department, showing that all 11 methods have passed. It lists details for tests such as "testng.Test1," "testng.Test3," "testng.Test2," and "testng.Test4," with various sub-tests related to car loans, logins, and mobile operations. The options include viewing test information and switching themes.

The report includes:

  • Summary: Number of tests passed, failed, or skipped.

  • Details: Failed test details (e.g., “expected true but found false” for the demo test).

  • Data Provider Results: Shows results for tests run with multiple data sets.

Check out the complete code repository below:

Real-World Applications

TestNG is widely used in automation frameworks for:

  • Selenium: Web automation (e.g., opening browsers, navigating URLs).

  • Appium: Mobile automation (e.g., starting the Appium server, interacting with apps).

  • REST API: API testing (e.g., setting base URLs, validating responses).

Example Scenarios

  1. Database Cleanup: Use @BeforeTest to delete data before running loan application tests.

  2. API Setup: Set the base URL in @BeforeTest for REST API tests.

  3. Mobile Testing: Start the Appium server in @BeforeTest before executing mobile tests.

  4. Screenshot on Failure: Use listeners to capture screenshots when Selenium/Appium tests fail.

  5. Regression Testing: Run all test cases in a package using the <packages> tag.

Best Practices for TestNG Frameworks

  1. Consistent Naming Conventions: Use prefixes like “mobile” or “api” for test methods to enable easy filtering with regular expressions.

  2. Modular Design: Organize test cases into modules (e.g., “Personal Loan”, “Car Loan”) for better control.

  3. Avoid Hardcoding: Use XML parameters or data providers to drive data like URLs, usernames, or API keys.

  4. Debugging: Read error messages carefully to identify and fix issues (e.g., missing quotes in XML).

  5. Smart Use of Include/Exclude: Use include when selecting a few tests and exclude when skipping a few to minimize XML complexity.

  6. Cautious Parallel Execution: Test parallel execution on a small scale to avoid performance issues.

Common Questions

  1. How do you skip a test case without commenting it out?

    • Use the enabled = false attribute in the @Test annotation.
  2. How do you achieve parameterization in TestNG?

    • Use XML-based parameters for global/test-level data or the @DataProvider annotation for test-specific data sets.
  3. How do you identify the failed test case in a listener?

    • Use the ITestResult object’s getName() method in the onTestFailure listener.
  4. Why use TestNG over plain Java?

    • TestNG provides annotations, dependency management, parallel execution, and detailed reporting, which plain Java lacks.

Conclusion

TestNG is a versatile and powerful testing framework that simplifies the process of building and executing test cases in Java-based automation projects. From setting up test environments to running tests with multiple data sets, TestNG offers a wide range of features that make it indispensable for framework development.

To deepen your understanding, practice these concepts with real automation code (e.g., Selenium, Appium, or REST API tests). You can also explore advanced reporting tools like Extent Reports for more visually appealing results.

Happy testing, and stay tuned for more automation tutorials!

0
Subscribe to my newsletter

Read articles from Samiksha Kute directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Samiksha Kute
Samiksha Kute

Passionate Learner!