TestNG merge data provider
This article presents a technique to enhance TestNG data provider. The goal is to implement a generic tool for merging data provider and feeding the test with those merged data.
Therefore, we must pass the list of data provider we want to merged, to a the merging tool
We implement::
- A tool for merging TestNG dataprovider : CustomDataProvider.mergeDataProvider().
- An annotation DataProviderArguments for passing arguments to mergeDataProvider().
- A sample of test SampleOfUsageOfDataProvider which is using a merged dataprovider.
CustomDataProvider.mergeDataProvider
This is the so called 'merging tool', it is wrapped as data provider because it is more handy to call it from a test case.
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.testng.annotations.DataProvider;
/**
* this method merge 2 or more data providers. It takes care of the combination.
* for example :
* zeroOne_DataProvider = | 1 , 0 |
* | 1 , 1 |
*
* addresses_DataProvider = | 127.0.0.1 |
* | fe80::8cf6:17ea:939b:7291 |
*
* resultDataProvider= |127.0.0.1 , 1 , 0 |
* |127.0.0.1 , 1 , 1 |
* |fe80::8cf6:17ea:939b:7291 , 1 , 0 |
* |fe80::8cf6:17ea:939b:7291 , 1 , 1 |
*
*
*
* Sample of usage:
* @Test(dataProviderClass = CustomDataProvider.class, dataProvider = "mergeDataProvider")
* @DataProviderArguments({
* "DataProviders,zeroOne_DataProvider",
* "DataProviders,addresses_DataProvider" })
*
*/
public class CustomDataProviders {
/**
* This method implements the merge DataProvider
*/
@DataProvider(name = "mergeDataProvider")
public static Object[][] mergeDataProvider(Method testMethod) throws Exception {
CustomDataProviders customDataProviders = new CustomDataProviders();
List<DataProviderLocation> listOfDataProviderLocation = customDataProviders
.resolveDataProviderMergeArguments(testMethod);
Object[][] totalDataProvider = new Object[0][0];
for (DataProviderLocation dataProviderLocation : listOfDataProviderLocation) {
Object[][] dataProviderToAdd = customDataProviders.retrieveDataProvider(dataProviderLocation);
totalDataProvider = customDataProviders.merge(totalDataProvider, dataProviderToAdd);
}
return totalDataProvider;
}
/**
* Implementation of the logic to take care of the combination
*/
private Object[][] merge(Object[][] dataProvider1, Object[][] dataProvider2) throws Exception {
int numberOfData1 = dataProvider1.length;
if (numberOfData1 == 0) {
return dataProvider2;
}
int numberOfParamters1 = dataProvider1[0].length;
int numberOfData2 = dataProvider2.length;
if (numberOfData2 == 0) {
return dataProvider1;
}
int numberOfParamters2 = dataProvider2[0].length;
Object[][] resultDataProvider = new String[numberOfData1 * numberOfData2][numberOfParamters1
+ numberOfParamters2];
int currentRowRP = 0;
for (int i = 0; i < numberOfData1; i++) {
for (int j = 0; j < numberOfData2; j++) {
for (int k = 0; k < numberOfParamters1; k++) {
resultDataProvider[currentRowRP][k] = dataProvider1[i][k];
}
for (int l = 0; l < numberOfParamters2; l++) {
resultDataProvider[currentRowRP][numberOfParamters1 + l] = dataProvider2[j][l];
}
currentRowRP++;
}
}
return resultDataProvider;
}
/**
* Retrieves DataProvider object using a DataProviderLocation
*/
private Object[][] retrieveDataProvider(DataProviderLocation dataProviderLocation) throws Exception {
// using reflection to call the method with the same name as the Data Provider
Method method = dataProviderLocation.getDataproviderClass().getMethod(
dataProviderLocation.getDataProviderName());
return (Object[][]) method.invoke(dataProviderLocation.getDataproviderClass());
}
/**
* Converts the DataProviderArguments annotation into a List<DataProviderLocation>
*/
private List<DataProviderLocation> resolveDataProviderMergeArguments(Method testMethod) throws Exception {
if (testMethod == null)
throw new IllegalArgumentException("Test Method context cannot be null.");
DataProviderArguments args = testMethod.getAnnotation(DataProviderArguments.class);
if (args == null)
throw new IllegalArgumentException("Test Method context has no DataProviderArguments annotation.");
if (args.value() == null || args.value().length == 0)
throw new IllegalArgumentException("Test Method context has a malformed DataProviderArguments annotation.");
ArrayList<DataProviderLocation> arguments = new ArrayList<DataProviderLocation>();
for (int i = 0; i < args.value().length; i++) {
String[] parts = args.value()[i].split(",");
arguments.add(this.new DataProviderLocation(parts[0], parts[1]));
}
return arguments;
}
/**
* inner class which defined a data provider location
*/
private class DataProviderLocation {
private Class<?> dataproviderClass;
private String dataProviderName;
public DataProviderLocation(String dataproviderClassName, String dataProviderName)
throws ClassNotFoundException {
super();
this.dataproviderClass = Class.forName(dataproviderClassName);
this.dataProviderName = dataProviderName;
}
public Class<?> getDataproviderClass() {
return dataproviderClass;
}
public String getDataProviderName() {
return dataProviderName;
}
}
}
DataProviderArguments
This annotation define parameters passed to a given Data provider. In this example, parameter is a array of String.
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
*
*
* Warning: Those arguments should map to a data provider :
* -defined statically
* -with the same name for the method and the data provider annotation
*
* For example:
* @DataProvider(name = "unsupportedWantBacks")
* public static Object[][] unsupportedWantBacks() {
*
* Sample of usage:
* @Test(dataProviderClass = CustomDataProvider.class, dataProvider = "mergeDataProvider")
* @DataProviderArguments({
* "com.automation.va.utils.dataProvider.ListOfDataProvider,va_addresses",
* "com.automation.va.tests.scvp.ScvpStats,unsupportedWantBacks" })
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface DataProviderArguments {
String[] value();
}
SampleOfUsageOfDataProvider
This code is an example of call made to the 'merging' tool
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class SampleOfUsageOfDataProvider {
@DataProvider(name = "wrongCredentials")
public static Object[][] wrongCredentials() {
return new Object[][] {
new Object[] {
"",
"" },
new Object[] {
"proxyUsername",
"wrongPassword" },
new Object[] {
"wrongUsername",
"proxyPassword" } };
}
@DataProvider(name = "addresses")
public static Object[][] addresses() {
return new Object[][] {
new Object[] { "localhost" },
new Object[] { "127.0.0.1" } };
}
@Test(dataProviderClass = CustomDataProviders.class, dataProvider = "mergeDataProvider")
@DataProviderArguments({ "SampleOfUsageOfDataProvider,wrongCredentials" })
public void basic1(String username, String password) throws Exception {
System.out.println("done");
}
@Test(dataProviderClass = CustomDataProviders.class, dataProvider = "mergeDataProvider")
@DataProviderArguments({"SampleOfUsageOfDataProvider,va_addresses", "SampleOfUsageOfDataProvider,wrongCredentials" })
public void basic2(String address, String username, String password) throws Exception {
System.out.println("done");
}
}