TestNG
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();
}
}