Testng: load external data

1 Overview

For a function or a process, given an input, it should return a result

Input refers to the combination of parameters or conditions, which constitutes different test scenarios

The result is the input processing of the program. Compare it with the expected result to see the correctness of the function of the current test scenario

When these data are stored in the form of external files, it is easy to modify and append without readjusting the code

2 @DataProvider

In Testng, @ DataProvider is responsible for providing data to the test method and returning the Object [] [] Object

Therefore, it can be considered that in the @ DataProvider method, the external file is read and the contents of the file are returned in the form of Object [] []

Scheme:

  1. Each method corresponds to its own dataProvider and reads its own data file; It is easy to implement, but the code is too redundant, and the data files are too scattered and read frequently
  2. All methods correspond to a dataProvider. The dataProvider selects the corresponding test data according to the called method name; The implementation is complex and needs to consider parallelism, but the code is concise
public class DataDriver {
    public static Map<String, Object[][]> data = null;

    @DataProvider(name = "dp")
    public static Object[][] getData(Method method) {
        if (data == null || data.get(method.getName()) == null) {
            return new Object[][] {};
        }
        return data.get(method.getName());
    }
}

The second method is selected here, but the external file is not read, and only data is selected by method name

Considering that there are too many test cases, if you load all case data into memory at one time, it will be too cumbersome, so you decide to load only the data of the current case

3 TestCase

@The BeforeClass stage initializes data, and the @ AfterClass stage reclaims data

When reading data, the tool class is encapsulated; According to different data carriers, you can write multiple sets of tool classes

All test methods point to the same dataProvider. Don't forget to add the dataProviderClass attribute

public class IpCase {
    @BeforeClass
    public void setup() {
        String caseFullName = this.getClass().getName();
        String caseName = caseFullName.substring(caseFullName.lastIndexOf('.') + 1);
        DataDriver.data = ExcelUtil.readExcel(caseName);
    }

    @AfterClass
    public void teardown() {
        DataDriver.data.clear();
    }

    @Test(dataProvider = "dp", dataProviderClass = DataDriver.class)
    public void isIpv41(String ip, boolean expected) {
        boolean actual = ip != null;
        Assert.assertEquals(actual, expected);
    }

    @Test(dataProvider = "dp", dataProviderClass = DataDriver.class)
    public void isIpv6(String ip, boolean expected) {
        boolean actual = ip != null;
        Assert.assertEquals(actual, expected);
    }
}

4 processing Excel files

For data storage, you can use Excel, TXT or database. Just select a carrier that is easy to maintain and read

Here, we use the poi package to parse excel

The name of excel shall be consistent with TestCase (IpCase.xlsx), and the sheet name corresponds to each test method (isIpv4, isIpv6), so that the corresponding test data can be obtained according to the method name

The corresponding data of isIpv4 is as follows. The first line is the title, which should be skipped during parsing

IPExpected
1.1.1.1TRUE
2.2.2.256FALSE

The corresponding data of isIpv6 are as follows

IPExpected
::1TRUE
FF01::1101TRUE
public class ExcelUtil {
    private static final String SUFFIX_2007 = ".xlsx";

    public static Map<String, Object[][]> readExcel(String caseName) {
        Map<String, Object[][]> result = new HashMap<>();
        InputStream is = null;

        try {
            is = ExcelUtil.class.getClassLoader().getResourceAsStream(String.format("TestCase/%s%s", caseName, SUFFIX_2007));
            XSSFWorkbook workbook = new XSSFWorkbook(is);

            for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {
                Sheet sheet = workbook.getSheetAt(sheetNum);

                int rows = sheet.getLastRowNum();
                if (rows <= 0) continue;
                int cols = sheet.getRow(1).getLastCellNum();
                Object[][] data = new Object[rows][cols];

                for (int rowNum = 1; rowNum <= rows; rowNum++) {
                    Row row = sheet.getRow(rowNum);
                    for (int celNum = 0; celNum < cols; celNum++) {
                        Cell cell = row.getCell(celNum);

                        Object cellValue = null;
                        CellType cellType = cell.getCellTypeEnum();
                        if (cellType == CellType.BOOLEAN) {
                            cellValue = cell.getBooleanCellValue();
                        } else if (cellType == CellType.STRING) {
                            cellValue = cell.getStringCellValue();
                        } else if (cellType == CellType.NUMERIC) {
                            cellValue = cell.getNumericCellValue();
                        } else {
                            cellValue = "";
                        }

                        data[rowNum - 1][celNum] = cellValue;
                    }
                }
                result.put(sheet.getSheetName(), data);
            }
        } catch (IOException e) {
            System.out.println("File not found: " + caseName);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
}

5 @Test annotation conversion

When the test method needs to load data, it has to specify the attribute DataProvider = "DP", dataproviderclass = datadriver class

To save time, you can use the IAnnotationTransformer listener

  • When the contract method name contains "dp", "U dp" or other suffixes you like, automatically add the dataProvider attribute to the @ Test annotation
  • Specify the group name, assuming that all test methods with group = dp automatically supplement the attribute
public class TestAnnotationTransformer implements IAnnotationTransformer {
    @Override
    public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
        String[] groups = annotation.getGroups();
        for(String group: groups) {
            if (group.equals("dp")) {
                annotation.setDataProvider("dp");
                annotation.setDataProviderClass(DataDriver.class);
                break;
            }
        }
    }
}

6 subsequent optimization

  • At present, there is only excel parsing tool class, and only the 2007 version is supported. The support of the following versions can be supplemented
  • Excel data type judgment, floating point number, date and other types need to be strengthened
  • When executing in parallel, datadriver Data may conflict, so you need to migrate the reference to the Case class

Keywords: Java unit testing Testing

Added by whitemoss on Wed, 05 Jan 2022 12:39:44 +0200