Customizing Test Names at Runtime in TestNG



TestNG is a highly flexible and powerful testing framework that allows testers to structure and execute their test cases with great versatility. While it's well-equipped to handle many testing scenarios, there are situations where dynamically renaming test methods during runtime becomes crucial. This functionality can be especially valuable in the following cases:

  • Improving test reports by assigning more descriptive and meaningful names to tests, making it easier to understand test outcomes at a glance.

  • Reflecting input values in test method names when executing parameterized tests, which helps in tracking how different sets of data affect the system.

  • Enhancing debugging by dynamically setting test names based on runtime conditions, thus making it simpler to diagnose failures.

One common challenge when working with TestNG is managing DataProviders, where each row of test data needs a unique name for the corresponding test method. Dynamically assigning test names in this scenario ensures clear identification and proper reporting for each set of input data.

In all of the examples below testName is a thread-local variable that stores a String value that is specific to each thread.


ThreadLocal<String> testName = new ThreadLocal<>();

 Let's see some of the ways to update the test method name at run time

1. Updating Test Name with Invocation Count Number

To dynamically update the test name with an invocation count number, your test class needs to implement the ITest interface and override the getTestName() method. This allows for more meaningful test names, especially in scenarios where parameterized tests are used.

@Override
public String getTestName() {
return testName.get();
}

@BeforeMethod
public void updateTestName(ITestContext testContext, Method method) {
if (!method.getDeclaredAnnotation(Test.class).dataProvider().isEmpty()) {
testName.set(method.getName() + "_" + Arrays.stream(testContext.getAllTestMethods()).filter(
m -> m.getMethodName().equalsIgnoreCase(method.getName())
).findFirst().get().getCurrentInvocationCount());
} else {
testName.set(method.getName());
}
}

The updateTestName method checks whether the test method is associated with a data provider. If it is, the test name is set dynamically by combining the method name with the current invocation count. This process involves:

  • Filtering through all test methods in the current test context to find the one that matches the name of the method being executed.

  • Extracting the current invocation count of that method.

  • Constructing a test name in the format: methodName_invocationCountNumber, and storing it in the testName thread-local variable.

If the test method doesn’t have a data provider, it simply sets the test name to the method's name. This approach is particularly useful when the data provider is an external source like an Excel sheet or CSV file, where the number of data sets is unknown. It ensures each test iteration gets a unique name based on the invocation count, making it easier to track results and debug issues during the test run. 

2. Updating Test Name with custom suffix using Invocation Count

This method builds upon the previous one, but with an added feature: it appends a custom suffix to the test name based on the invocation count. The logic works as follows: if the invocation count is 0 (indicating the test is being run for the first time), it appends the suffix "alpha". If the invocation count is 1, it appends the suffix "beta". This example assumes the data provider is supplying exactly two rows of data.
 
@BeforeMethod
public void updateTestName(ITestContext testContext, Method method) {
if (!method.getDeclaredAnnotation(Test.class).dataProvider().isEmpty()) {
int invocationCount = Arrays.stream(testContext.getAllTestMethods()).filter(
m -> m.getMethodName().equalsIgnoreCase(method.getName())
).findFirst().orElseThrow().getCurrentInvocationCount();
String suffix = "";
if (invocationCount == 0) {
suffix = "alpha";
} else if (invocationCount == 1) {
suffix = "beta";
}
testName.set(method.getName() + "_" + suffix);
} else {
testName.set(method.getName());
}
}

In this approach:

  • The method checks if the test method is associated with a data provider.

  • It then calculates the invocation count by filtering the current test methods to find the method name and retrieving its invocation count.

  • Based on the invocation count, it assigns a custom suffix ("alpha" or "beta").

  • The final test name is constructed by appending the appropriate suffix to the method name, giving a unique identifier to each test execution iteration.

This technique is especially helpful when you want to differentiate between multiple runs of the same test in a more descriptive manner. It is particularly useful in scenarios where the test involves iterations over a fixed set of input data (e.g., two rows from a data provider).

3. Update TestName with Values Provided in the Data Provider

This method focuses on dynamically setting the test name based on the data provided by the data provider. It first checks if the test method is associated with a data provider. If a data provider is used, the test name is updated to include both the method name and the first element from the provided data.

The steps involved in this process are:

  • Extracting the first element (or name type) from the data provided in the data provider array. In this case, testData is the data provider supplying the input values.
  • Constructing the test name by combining the method name with the extracted data value, using the format: methodName_nameType.
  • If the test method does not have an associated data provider, the test name is simply set to the method name itself.

This approach is particularly useful when running tests multiple times with different sets of data. By including the specific data value in the test name, it makes it much easier to identify individual test iterations in reports or logs, ensuring better traceability and facilitating debugging. This method helps in uniquely identifying each test execution, which is crucial for large-scale testing with multiple input scenarios.

 
@BeforeMethod
public void updateTestName(Method method, Object[] dataProvided) {
if (!method.getDeclaredAnnotation(Test.class).dataProvider().isEmpty()) {
String nameType = dataProvided[0].toString();
testName.set(method.getName() + "_" + nameType);
} else {
testName.set(method.getName());
}
}

@DataProvider(name = "testData")
public Object[][] testData() {
return new Object[][] {
{"data1","superman", "alpha"},
{"data2","batman", "beta"}
};
}

Dynamically changing test names in TestNG is a powerful feature that significantly improves reporting, debugging, and data-driven testing capabilities. By adjusting test names during runtime, you can make your test results more descriptive, which enhances the clarity of test reports and simplifies the process of identifying issues. This technique is especially beneficial in large-scale automation projects where organizing and categorizing numerous tests can be challenging. It provides better visibility into test execution, making it easier to track individual test progress and pinpoint failures quickly.

This flexibility also aids in implementing data-driven testing, where each test case can reflect different input parameters dynamically, offering clearer insights into how various inputs affect the system. TestNG’s dynamic test naming capabilities allow you to generate unique identifiers for each execution, which can be invaluable for reporting purposes.

For a complete code example, please refer to the GitHub repository. If you have any questions or use cases to share, don't hesitate to leave a comment below. Wishing you the best with your testing efforts! 🚀

Post a Comment

Previous Post Next Post