Writing a data provider class taking Hashmap as collection and passing it for multiple parameters in API Test

icantcode :

I am trying to minimize the line of code in my Tests by writing a reusable component in the form of TestNG DataProvider. My Test specification which needs to be sent to the server accepts a Map>.

 @Test(dataProvider = "provideData")
 public void TestMethod(Map<String,Object> map) throws Exception {
RequestSpecification spec = generateCommonReqSpecJsonWithQueryParams(map);
Response res = RestOperationUtils.get(url, spec, null);
}


@DataProvider(name="provideData")
    public static Object[][] getData() throws Exception {
        Map<String, ArrayList<String>> map = new HashMap<>();
        ArrayList<String> a1 = new ArrayList<>();
        a1.add("First Value");
        a1.add("Second Value);
        a1.add("Third Value");
        a1.add("Fourth Value");
        map.put("Test[]", a1);
        map.put("month_start", new ArrayList(Arrays.asList("2019-06-01")));
        map.put("month_end", new ArrayList(Arrays.asList("2019-06-30")));
        map.put("viewers[]", new ArrayList(Arrays.asList("ESPN")));
        ArrayList<String> b1 = new ArrayList<>();
        b1.add("Fifth Value");
        b1.add("Sixth Value");
        b1.add("Seventh Value");
        map.put("Result[]", b1);

Since TestNG, mandates us to return Object[][] from DataProvider, here are the different approaches i have tried:

Approach 1:

String[] keys = new String[map.size()];
        ArrayList<ArrayList<String>> values = new ArrayList<>();
        int index = 0;
        for (Map.Entry<String, ArrayList<String>> mapEntry : map.entrySet()) {
            keys[index] = mapEntry.getKey();
            values.add(index, (ArrayList<String>) mapEntry.getValue());
            //   x[index] = mapEntry.getValue();
            index++;
        }
        Object[][] result = new Object[values.size()][];
        index = 0;
        int index2;
        for (List<String> list : values) {
            result[index] = new Object[list.size()];
            index2 = 0;
            for (String item : list) {
                result[index][index2] = item;
                index2++;
            }
            index++;
        }
return result ;

Approach 2:

     Object[][] arr = new Object[map.size()][2];
        Set entries = map.entrySet();
        Iterator entriesIterator = entries.iterator();
        int i = 0;
        while(entriesIterator.hasNext()){
            Map.Entry mapping = (Map.Entry) entriesIterator.next();
            arr[i][0] = mapping.getKey();
            arr[i][1] = mapping.getValue();
            i++;
        }
        return arr;

Approach 3:

Simply return the following:

return new Object[][] {{map}};

Approach 1: Using approach 1 it gives 5 expected parameters, but since i need map to be passed in my spec as queryParameters, i'm finding it difficult to use it in my Test method on how to read them from DataProvider class.

Approach 2: It gives me back 2 parameters, and with Map<String,Object> map as parameter it accepts only 1.

Approach 3: I did not understand why/how it worked but upon debugging found it is being expected as a special case for TestNg library else we need to use approach 1/2 to convert hashmap to Object[][].

Please let me know if any other information is required in this query.

theawesometrey :

Approach 3 seems perfectly valid to me. The object[][] is just a way of holding all test cases where each object[] index would just be the test case. Each test case should then match the number and type of arguments expected by the test method.

Object[][] is chosen to hold the test cases since all objects in java either extend from Object, or in the case of primitives can be auto boxed into its object form which does extend from Object.

TestNG will then handle hooking up the data provider to each of its tests, as well as applying and casting the test case parameters for each.

For example:

@Test(dataProvider="getTestCases")
public void test(List<Integer> list, double d){
    // test code
}

Would expect something like:

@DataProvider
public Object[][] getTestCases(){
    return new Object[][] {
        {Arrays.asList(1, 2, 3), 1.0},
        {Arrays.asList(4, 5, 6), 2.0}
    };
}

Where {Arrays.asList(1, 2, 3), 1.0} would be test case 1 and {Arrays.asList(4, 5, 6), 2.0} would be test case 2.

EDIT:

To address the code change for cleaning up the data provider the following is what was presented by Holger:

@DataProvider(name="provideData")
public static Object[][] getData() {
    Map<String, List<String>> map = new HashMap<>();
    map.put("Test[]", Arrays.asList("First Value", "Second Value", "Third Value", "Fourth Value"));
    map.put("month_start", Arrays.asList("2019-06-01"));
    map.put("month_end", Arrays.asList("2019-06-30"));
    map.put("viewers[]", Arrays.asList("ESPN"));
    map.put("Result[]", Arrays.asList("Fifth Value", "Sixth Value", "Seventh Value"));
    return new Object[][]{{map}};
}

As for the reason why approach 1 and 2 are not working for you, it has to do with the mismatch of DataProvider type/number of returned test cases. Your test expects a map to be provided to it

public void TestMethod(Map<String,Object> map) 

which expects one parameter of type Map<String,Object> from the data provider, however you are attempting to pass a string and variable size stings for approach 1 or a string and list of strings for approach 2. Both of which are different types and number of parameters than the one map.

I would recommend changing the test to accept Map<String, List<String>> to promote more clarity in the test, unless however the latter is what is needed by the function being tested itself.

In testing, simplicity is preferred because if you add too much complexity to the test itself. The test can become more error prone than the thing it is testing. Thus the simple map return should be sufficient.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=163163&siteId=1