mardi 29 octobre 2013

TestNG merge data provider

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");
    }
}

Aucun commentaire:

Enregistrer un commentaire