Search code examples
selenium-webdrivercollectionshashmaptestngtestng-dataprovider

Hashmap in testNG dataprivider to reduce arguments passing to test method


I've been writing Test scripts using TestNG's DataProvider feature. Till now I was happy with the way I was doing it as the number of parameters I was passing was less than 10, but some of the new pages I am testing have more than 30-35 parameters, adding these parameters to the Test Method makes it look really ugly

Its a good way of dynamically passing parameters, but then using them is difficult as you have to be really careful with the indexes and prone to errors if this has to be done for a lot of methods.

I tried passing the test data as a ArrayList>, where the HashMap is the Column Name-Value pair but TestNG wont accept this as HashMap cant be cast as Object[]. The reason I thought of using HashMap is because you can query a key and get its value and the key is known to me. I could have written a common method to get the value and assign it to the variable representing the names of the fields on the page.

The Data Source (XLS) only stores the test data that is entered on the page, so its purely data-driven and not keyword based. All the pages I am testing are data entry pages.

What is the best way to have some kind of identity on the test data and also get TestNG to accept it.

As an alternative I created a map while taking data from Excel and passing to data provider at the same time I created a hashmap and stored it globally to access in my test method but this way won't remove passing arguments to my test method

Any help in this regards is really appreciated. Thanks Ketan


Solution

  • I tried passing the test data as a ArrayList>, where the HashMap is the Column Name-Value pair but TestNG wont accept this as HashMap cant be cast as Object[].

    TestNG can very well work with a data driven method that uses a Map as a parameter which is fed by a data provider. The below sample should clarify that.

    In this example I have an excel spreadsheet, in which there's a sheet with its name as "53799150" and whose data looks like below

    +------------+------------+-------------+-----------------+
    | TestcaseId | RollNumber | StudentName | StudentLocation |
    +------------+------------+-------------+-----------------+
    |          1 | S1001      | Po          | Bengaluru       |
    |          2 | S1002      | Oogway      | Chennai         |
    |          3 | S1003      | Shifu       | Delhi           |
    |          4 | S1004      | TaiLung     | Kolkata         |
    +------------+------------+-------------+-----------------+
    

    The below sample works with the above shown data as a map:

    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
    import org.apache.poi.ss.usermodel.Cell;
    import org.apache.poi.ss.usermodel.DataFormatter;
    import org.apache.poi.ss.usermodel.Row;
    import org.apache.poi.ss.usermodel.Sheet;
    import org.apache.poi.ss.usermodel.Workbook;
    import org.apache.poi.ss.usermodel.WorkbookFactory;
    import org.testng.annotations.DataProvider;
    import org.testng.annotations.Test;
    
    public class MapPoweredDataProviderSample {
    
      private static final String FILE = "src/test/resources/53799150.xlsx";
      private static final DataFormatter dataFormatter = new DataFormatter();
      private static final String SHEET_NAME = "53799150";
    
      @Test(dataProvider = "dp")
      public void testMethod(Map<String, String> data) {
        System.err.println(data);
      }
    
      @DataProvider(name = "dp")
      public Object[][] getData() throws IOException, InvalidFormatException {
        Workbook workbook = WorkbookFactory.create(new File(FILE));
        Sheet sheet = workbook.getSheet(SHEET_NAME);
        Iterable<Row> rows = sheet::rowIterator;
        List<Map<String, String>> results = new ArrayList<>();
        boolean header = true;
        List<String> keys = null;
        for (Row row : rows) {
          List<String> values = getValuesInEachRow(row);
          if (header) {
            header = false;
            keys = values;
            continue;
          }
          results.add(transform(keys, values));
        }
        return asTwoDimensionalArray(results);
      }
    
      private static Object[][] asTwoDimensionalArray(List<Map<String, String>> interimResults) {
        Object[][] results = new Object[interimResults.size()][1];
        int index = 0;
        for (Map<String, String> interimResult : interimResults) {
          results[index++] = new Object[] {interimResult};
        }
        return results;
      }
    
      private static Map<String, String> transform(List<String> names, List<String> values) {
        Map<String, String> results = new HashMap<>();
        for (int i = 0; i < names.size(); i++) {
          String key = names.get(i);
          String value = values.get(i);
          results.put(key, value);
        }
        return results;
      }
    
      private static List<String> getValuesInEachRow(Row row) {
        List<String> data = new ArrayList<>();
        Iterable<Cell> columns = row::cellIterator;
        for (Cell column : columns) {
          data.add(dataFormatter.formatCellValue(column));
        }
        return data;
      }
    }
    

    Here's the output:

    {TestcaseId=1, RollNumber=S1001, StudentName=Po, StudentLocation=Bengaluru}
    {TestcaseId=2, RollNumber=S1002, StudentName=Oogway, StudentLocation=Chennai}
    {TestcaseId=3, RollNumber=S1003, StudentName=Shifu, StudentLocation=Delhi}
    {TestcaseId=4, RollNumber=S1004, StudentName=TaiLung, StudentLocation=Kolkata}
    
    ===============================================
    Default Suite
    Total tests run: 4, Passes: 4, Failures: 0, Skips: 0
    ===============================================