DataProvider with spring

459 views
Skip to first unread message

Itay Goz

unread,
Apr 19, 2021, 9:21:54 AM4/19/21
to testng-users

Hi, I want to load data provider in a separate class with beans dependency.

So the problem is that I can't use static, and when the test tries to load the data it fails on null pointer exception.

@DataProvider(name = "validMobile")
    public Object[][] validMobile() throws Exception {
        ExcelSheetProperties excelProps = ExcelSheetProperties.builder().sheetName("ValidMobile").lastRow(3)
                .lastColumn(3).build();
        return excelUtils.getExcelSheetAsMatrix(excelProps);
    }

    @Test(dataProvider = "validMobile"dataProviderClass = AbstractUserManagementTestNGContext.class)
    public void testMobileAndFixValidation(String inputPhoneString expectedString messagethrows Exception {
        String validatedPhone = validationService.validatePhone(inputPhone);

        Assert.assertEquals(validatedPhone, expected);
    }


⇜Krishnan Mahadevan⇝

unread,
Apr 19, 2021, 10:33:59 AM4/19/21
to testng-users
Why cant you use static ? I didnt understand that part.

The data provider when you use the dataproviderclass attribute of the "@Test" annotation will be instantiated by TestNG and not by Spring. So it shouldnt be a problem.
It would be good if you could please elaborate the problem statement a bit.

Thanks & Regards
Krishnan Mahadevan

"All the desirable things in life are either illegal, expensive, fattening or in love with someone else!"
My Scribblings @ http://wakened-cognition.blogspot.com/
My Technical Scribblings @ https://rationaleemotions.com/


--
You received this message because you are subscribed to the Google Groups "testng-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to testng-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/testng-users/d16a78ad-b770-452a-9b6e-83e9f13045c1n%40googlegroups.com.

Itay Goz

unread,
Apr 20, 2021, 3:42:54 AM4/20/21
to testng...@googlegroups.com
Thanks for the fast reply.
In my case I want to load excel file from the data provider, and create instance for each file.
Moreover, when I wanna read from the same file but different sheets static makes it impossible for working in multiple threads.
So my solution is to work with a solid framework that takes care of the instances. The data provider works fine if it's in the same file.
But I want to separate it to other class, and then it won't work because testng tries to load the class before spring and it cannot be static.

⇜Krishnan Mahadevan⇝

unread,
Apr 20, 2021, 4:17:15 AM4/20/21
to testng-users
I understand that intent of you leveraging the Dependency Injection capabilities of Spring to manage your instances.

In the case of a data provider, TestNG just loads the class and then uses reflection to instantiate the class.

Can you please share a sample project that can be used to reproduce the problem that you are experiencing?

Thanks & Regards
Krishnan Mahadevan

"All the desirable things in life are either illegal, expensive, fattening or in love with someone else!"
My Scribblings @ http://wakened-cognition.blogspot.com/
My Technical Scribblings @ https://rationaleemotions.com/

--
You received this message because you are subscribed to the Google Groups "testng-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to testng-users...@googlegroups.com.

Itay Goz

unread,
Apr 21, 2021, 3:47:17 AM4/21/21
to testng...@googlegroups.com
For instance, I want this part to be in a separate class, the dataprovider class:
public class ValidationDataProvider {
    @Autowired
    private ExcelUtils excelUtils;

    @BeforeClass(alwaysRun = true)
    private void init() {
        URL fileToRead = Thread.currentThread().getContextClassLoader().getResource("ValidationServiceTests.xlsx");
        excelUtils.init(fileToRead.getPath());
    }

    @AfterClass(alwaysRun = true)
    private void shutdown() {
        excelUtils.shutdown();
    }

    @DataProvider(name = "validMobile")
    public Object[][] validMobile() throws Exception {
        ExcelSheetProperties excelProps = ExcelSheetProperties.builder().sheetName("ValidMobile").lastRow(3)
                .lastColumn(3).build();
        return excelUtils.getExcelSheetAsMatrix(excelProps);
    }

    @DataProvider(name = "invalidMobile")
    public Object[][] invalidMobile() throws Exception {
        ExcelSheetProperties excelProps = ExcelSheetProperties.builder().sheetName("InvalidMobile").lastRow(5)
                .lastColumn(2).build();
        return excelUtils.getExcelSheetAsMatrix(excelProps);
    }
}

And this is the exelUtils:
@Component
public class ExcelUtils {

    private XSSFWorkbook excelWBook;

    private FileInputStream excelFile = null;

    public void init(String path) {
        try {
            System.out.println("Open: " + path);
            excelFile = new FileInputStream(path);
            // Access the required test data sheet
            excelWBook = new XSSFWorkbook(excelFile);
        } catch (FileNotFoundException e) {
            System.out.println("Could not read the Excel sheet");
            e.printStackTrace();
        } catch (IOException e) {
            System.out.println("Could not read the Excel sheet");
            e.printStackTrace();
        }
    }

    public void shutdown() {
        try {
            System.out.println("Attempt to close Excel sheet");
            excelFile.close();
        } catch (IOException e) {
            System.out.println("Could not close the Excel sheet");
            e.printStackTrace();
        }
    }

    public Object[][] getExcelSheetAsMatrix(ExcelSheetProperties excelPropsthrows Exception {

        String[][] mat = null;
        XSSFSheet excelWSheet = excelWBook.getSheet(excelProps.getSheetName());

        int startRow = excelProps.getFirstRow() - 1;
        int startCol = excelProps.getFirstColumn() - 1;

        int totalRows = excelProps.getLastRow() - 1;
        int totalCols = excelProps.getLastColumn() - 1;
        
        mat = new String[totalRows][totalCols + 1];

        int ci = 0, cj = 0;
        for (int i = startRow; i <= totalRows; i++, ci++) {
            cj = 0;
            for (int j = startCol; j <= totalCols; j++, cj++) {
                mat[ci][cj] = getCellData(i, j, excelWSheet);
            }
        }

        return mat;
    }

    private String getCellData(int rowNumint colNumXSSFSheet excelWSheetthrows Exception {
        XSSFCell cell;

        try {
            cell = excelWSheet.getRow(rowNum).getCell(colNum);

            if (cell.getCellType() == Cell.CELL_TYPE_BLANK) {
                return "";
            }

            return cell.getStringCellValue();

        } catch (Exception e) {
            System.out.println(e.getMessage());
            throw e;
        }
    }
}

And the usage of the dataprovider should be as this:
    @Test(dataProvider = "validMobile"groups = REGRESSION, dataProviderClass = ValidationDataProvider.class)
    public void testMobileAndFixValidation(String inputPhoneString expectedString messagethrows Exception {
        String validatedPhone = validationService.validatePhone(inputPhone);

        Assert.assertEquals(validatedPhone, expected);
    }


‫בתאריך יום ג׳, 20 באפר׳ 2021 ב-11:17 מאת ‪⇜Krishnan Mahadevan⇝‬‏ <‪krishnan.ma...@gmail.com‬‏>:‬
You received this message because you are subscribed to a topic in the Google Groups "testng-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/testng-users/kXLQFLkjj1A/unsubscribe.
To unsubscribe from this group and all its topics, send an email to testng-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/testng-users/CANikZL%3DfG0Ry_gsZvadLJgco__LkdTrK99dFoWwDtpc2dXPRhg%40mail.gmail.com.

Itay Goz

unread,
Apr 21, 2021, 3:52:14 AM4/21/21
to testng...@googlegroups.com
The component should be scope("prototype") in order to create instance for each injection.

Thanks

‫בתאריך יום ד׳, 21 באפר׳ 2021 ב-10:46 מאת ‪Itay Goz‬‏ <‪itay...@gmail.com‬‏>:‬

⇜Krishnan Mahadevan⇝

unread,
Apr 21, 2021, 6:23:45 AM4/21/21
to testng-users
Ok got it. Can you please do the following :

  1. Define a static method called getInstance() that will return back the current instance within ExcelUtils.
  2. Invoke the getInstance() within your TestNG managed data provider instance.

@Component
public class ExcelUtils {

    private static ExcelUtils instance;
private static void setInstance(ExcelUtils excelUtilsInstance) {
   instance = excelUtilsInstance;
}
public static ExcelUtils getInstance() {
return instance;
}
public ExcelUtils() {
   setInstance(this);
}
//rest of the code goes here.

}


Thanks & Regards
Krishnan Mahadevan

"All the desirable things in life are either illegal, expensive, fattening or in love with someone else!"
My Scribblings @ http://wakened-cognition.blogspot.com/
My Technical Scribblings @ https://rationaleemotions.com/

Itay Goz

unread,
Apr 21, 2021, 3:12:57 PM4/21/21
to testng...@googlegroups.com
Would it work with @Scope("prototype") ?
Because I want to create instance for each Excel file I read.

‫בתאריך יום ד׳, 21 באפר׳ 2021 ב-13:23 מאת ‪⇜Krishnan Mahadevan⇝‬‏ <‪krishnan.ma...@gmail.com‬‏>:‬

Itay Goz

unread,
Apr 22, 2021, 1:07:40 AM4/22/21
to testng...@googlegroups.com
I've tried your solution, and still not working...
I'm getting nullPointerException while trying to get the excel file.
Seems like it takes other instance each time

‫בתאריך יום ד׳, 21 באפר׳ 2021 ב-22:12 מאת ‪Itay Goz‬‏ <‪itay...@gmail.com‬‏>:‬

⇜Krishnan Mahadevan⇝

unread,
Apr 22, 2021, 1:26:31 AM4/22/21
to testng-users
Itay,
Am kind of trying to understand why does your ExcelUtils need to be a Spring managed bean, when:

  • You want each instance of this class to align itself to an excel spreadsheet file (.xls|.xlsx)
  • Your intent to use this class is ONLY with a data provider.

Can't you just make getExcelSheetAsMatrix() as a static method, which knows how to open an excel spreadsheet, read the sheet of your choice and return back the data ?

Trying to understand why does it have to be a component (Singleton scoped (or) prototype scoped for that matter)

Thanks & Regards
Krishnan Mahadevan

"All the desirable things in life are either illegal, expensive, fattening or in love with someone else!"
My Scribblings @ http://wakened-cognition.blogspot.com/
My Technical Scribblings @ https://rationaleemotions.com/

Itay Goz

unread,
Apr 22, 2021, 2:44:28 AM4/22/21
to testng...@googlegroups.com
I want to make instance for each excel file.
I've came up with a solution to store a map with the file path and instance:

public class ExcelUtils {

    private static HashMap<StringExcelUtilsinstances = new HashMap<>();
...
    public static void setInstance(String fileExcelUtils instance) {
        instances.put(file, instance);
        instance.init(file);
    }

    public static ExcelUtils getInstance(String file) {
        ExcelUtils instance = instances.get(file);

        if (instance == null) {
            instance = new ExcelUtils();
            setInstance(file, instance);
        }

        return instance;
    }
...


use like this:

public class DefaultValidationDataProvider {

    private URL fileToRead = Thread.currentThread().getContextClassLoader().getResource("dataprovider/DefaultValidationServiceTests.xlsx");
    private ExcelUtils excelUtils = ExcelUtils.getInstance(fileToRead.getPath());
    
    @AfterClass(alwaysRun = true)
    private void shutdown() {
        excelUtils.shutdown();
    }
....

It works wonderful without spring.



‫בתאריך יום ה׳, 22 באפר׳ 2021 ב-8:26 מאת ‪⇜Krishnan Mahadevan⇝‬‏ <‪krishnan.ma...@gmail.com‬‏>:‬

Eric Angeli

unread,
May 8, 2021, 7:42:34 PM5/8/21
to testng-users
So the short answer is no.

The long answer is yes. It involves capturing the ApplicationContext and then using the getAutowireCapableBeanFactory() to get the bean factory which you can then use to getBean(). But since this is outside of the ApplicationContext, you will have to implement your own method to actually do the autowiring. You could write a generic method to collect the autowire annotated fields and then use getBean to initialize them, or explicitly know the fields and set them. You could also implement an annotation processor to do it automatically. I did this a few years ago, but it is really out of date at this point. If you are interested I can see if I can find that old code and try to update it for a more recent version of Spring.

Eric

Reply all
Reply to author
Forward
0 new messages