TestNG

TestingUnit TestingJavaAnnotationsNext Generation

Testing Tool

TestNG

Overview

TestNG stands for "Next Generation" and is a Java testing framework inspired by JUnit and NUnit. It's designed to simplify a broad range of testing needs, from unit testing to integration testing and end-to-end testing. TestNG provides powerful features including rich annotations, parallel execution support, data-driven testing, and test dependency management, making it suitable for enterprise-level complex testing scenarios.

Details

TestNG's greatest strength lies in its flexibility and extensibility. It provides a rich set of annotations including @BeforeSuite, @AfterSuite, @DataProvider, @Parameters, and @Test, allowing fine-grained control over test execution order and dependencies. The grouping feature enables logical organization of tests for efficient management of large test suites.

For data-driven testing, it can dynamically load test data from external data sources such as CSV files, Excel sheets, and databases. The parallel execution feature enables concurrent execution at method, class, and suite levels, achieving significant time savings. Additionally, TestNG.xml files externalize test configuration, allowing flexible changes to test combinations at runtime.

Pros and Cons

Pros

  • Rich Annotations: Over 30 annotations for comprehensive test control
  • Parallel Execution: Support for concurrent execution at method, class, and suite levels
  • Data-Driven Testing: Dynamic test data loading from external data sources
  • Dependency Management: Ability to define dependencies between test methods
  • Grouping: Logical grouping and selective execution of tests
  • Reporting: Automatic generation of HTML and XML reports

Cons

  • Learning Curve: More complex configuration required compared to JUnit
  • Configuration Files: TestNG.xml syntax can be challenging for beginners
  • Java Only: Cannot be used with other programming languages
  • Overhead: May be over-featured for small-scale projects

Reference Pages

Code Examples

Hello World

// pom.xml
<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>7.8.0</version>
    <scope>test</scope>
</dependency>

// HelloWorldTest.java
import org.testng.Assert;
import org.testng.annotations.Test;

public class HelloWorldTest {
    
    @Test
    public void testGreeting() {
        String result = greet("TestNG");
        Assert.assertEquals(result, "Hello, TestNG!");
    }
    
    private String greet(String name) {
        return "Hello, " + name + "!";
    }
}

Basic Testing

import org.testng.Assert;
import org.testng.annotations.*;

public class CalculatorTest {
    private Calculator calculator;
    
    @BeforeClass
    public void setUp() {
        System.out.println("Test class starting");
    }
    
    @BeforeMethod
    public void beforeEachTest() {
        calculator = new Calculator();
    }
    
    @Test(priority = 1)
    public void testAddition() {
        int result = calculator.add(5, 3);
        Assert.assertEquals(result, 8, "Addition is incorrect");
    }
    
    @Test(priority = 2, dependsOnMethods = "testAddition")
    public void testSubtraction() {
        int result = calculator.subtract(10, 4);
        Assert.assertEquals(result, 6, "Subtraction is incorrect");
    }
    
    @Test(expectedExceptions = ArithmeticException.class)
    public void testDivisionByZero() {
        calculator.divide(10, 0);
    }
    
    @Test(timeout = 2000)
    public void testPerformance() {
        // Test should complete within 2 seconds
        calculator.complexCalculation();
    }
    
    @AfterMethod
    public void afterEachTest() {
        calculator = null;
    }
    
    @AfterClass
    public void tearDown() {
        System.out.println("Test class finished");
    }
}

Mocking

import org.testng.Assert;
import org.testng.annotations.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Mockito;

public class UserServiceTest {
    
    @Mock
    private UserRepository userRepository;
    
    private UserService userService;
    
    @BeforeMethod
    public void setUp() {
        MockitoAnnotations.openMocks(this);
        userService = new UserService(userRepository);
    }
    
    @Test
    public void testFindUserById() {
        // Setup mock
        User mockUser = new User(1L, "John Doe", "[email protected]");
        Mockito.when(userRepository.findById(1L)).thenReturn(mockUser);
        
        // Execute test
        User result = userService.findUserById(1L);
        
        // Verify
        Assert.assertNotNull(result);
        Assert.assertEquals(result.getName(), "John Doe");
        Assert.assertEquals(result.getEmail(), "[email protected]");
        
        // Verify mock method was called
        Mockito.verify(userRepository).findById(1L);
    }
    
    @Test
    public void testCreateUser() {
        User newUser = new User(null, "Jane Smith", "[email protected]");
        User savedUser = new User(2L, "Jane Smith", "[email protected]");
        
        Mockito.when(userRepository.save(newUser)).thenReturn(savedUser);
        
        User result = userService.createUser(newUser);
        
        Assert.assertEquals(result.getId(), Long.valueOf(2L));
        Assert.assertEquals(result.getName(), "Jane Smith");
        
        Mockito.verify(userRepository).save(newUser);
    }
}

Async Testing

import org.testng.Assert;
import org.testng.annotations.Test;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class AsyncOperationTest {
    
    @Test
    public void testAsyncOperation() throws Exception {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000); // Simulate 1 second processing
                return "Async operation completed";
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return "Error";
            }
        });
        
        String result = future.get(2, TimeUnit.SECONDS);
        Assert.assertEquals(result, "Async operation completed");
    }
    
    @Test(threadPoolSize = 3, invocationCount = 10)
    public void testConcurrentExecution() {
        // Concurrent test executed 10 times with 3 threads
        long threadId = Thread.currentThread().getId();
        System.out.println("Executing on thread ID: " + threadId);
        
        // Some concurrent processing test
        Assert.assertTrue(true);
    }
}

Setup and Teardown

import org.testng.annotations.*;

public class DatabaseTest {
    private DatabaseConnection connection;
    private TestDataBuilder dataBuilder;
    
    @BeforeSuite
    public void globalSetUp() {
        System.out.println("=== Test Suite Starting ===");
        // Database configuration, environment setup, etc.
    }
    
    @BeforeTest
    public void testSetUp() {
        System.out.println("Pre-test configuration");
        connection = DatabaseConnection.getInstance();
    }
    
    @BeforeClass
    public void classSetUp() {
        System.out.println("Class-level setup");
        dataBuilder = new TestDataBuilder();
    }
    
    @BeforeMethod
    public void methodSetUp() {
        // Before each test method execution
        connection.beginTransaction();
        dataBuilder.createTestData();
    }
    
    @Test
    public void testUserCreation() {
        User user = new User("Test User", "[email protected]");
        Long userId = connection.saveUser(user);
        
        Assert.assertNotNull(userId);
        Assert.assertTrue(userId > 0);
    }
    
    @Test
    public void testUserRetrieval() {
        Long userId = dataBuilder.getTestUserId();
        User user = connection.findUserById(userId);
        
        Assert.assertNotNull(user);
        Assert.assertEquals(user.getName(), "Test User");
    }
    
    @AfterMethod
    public void methodTearDown() {
        // After each test method execution
        connection.rollback();
        dataBuilder.cleanTestData();
    }
    
    @AfterClass
    public void classTearDown() {
        System.out.println("Class-level cleanup");
        dataBuilder = null;
    }
    
    @AfterTest
    public void testTearDown() {
        System.out.println("Post-test cleanup");
        connection.close();
    }
    
    @AfterSuite
    public void globalTearDown() {
        System.out.println("=== Test Suite Finished ===");
        // Global cleanup
    }
}

Advanced Features

import org.testng.Assert;
import org.testng.annotations.*;

public class AdvancedTestNGFeatures {
    
    // Data-driven testing using DataProvider
    @DataProvider(name = "testData")
    public Object[][] createTestData() {
        return new Object[][] {
            {"apple", 5},
            {"banana", 6},
            {"cherry", 6}
        };
    }
    
    @Test(dataProvider = "testData")
    public void testStringLength(String input, int expectedLength) {
        Assert.assertEquals(input.length(), expectedLength);
    }
    
    // Parameterized testing
    @Parameters({"browser", "url"})
    @Test
    public void testWithParameters(String browser, String url) {
        System.out.println("Browser: " + browser + ", URL: " + url);
        Assert.assertNotNull(browser);
        Assert.assertNotNull(url);
    }
    
    // Grouped testing
    @Test(groups = {"smoke"})
    public void smokeTest1() {
        System.out.println("Smoke test 1 executed");
        Assert.assertTrue(true);
    }
    
    @Test(groups = {"smoke", "regression"})
    public void smokeTest2() {
        System.out.println("Smoke test 2 executed");
        Assert.assertTrue(true);
    }
    
    @Test(groups = {"regression"})
    public void regressionTest() {
        System.out.println("Regression test executed");
        Assert.assertTrue(true);
    }
    
    // Group setup and teardown
    @BeforeGroups("smoke")
    public void beforeSmokeTests() {
        System.out.println("Preparation before smoke tests");
    }
    
    @AfterGroups("smoke")
    public void afterSmokeTests() {
        System.out.println("Cleanup after smoke tests");
    }
    
    // Custom assertions
    @Test
    public void testCustomAssertion() {
        String actual = "Hello World";
        String expected = "Hello World";
        
        // Execute multiple assertions with soft assert
        SoftAssert softAssert = new SoftAssert();
        softAssert.assertEquals(actual, expected, "Strings do not match");
        softAssert.assertTrue(actual.contains("Hello"), "Hello not found");
        softAssert.assertTrue(actual.contains("World"), "World not found");
        
        // Evaluate all assertions at the end
        softAssert.assertAll();
    }
}