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:
- 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
- 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
IP | Expected |
---|---|
1.1.1.1 | TRUE |
2.2.2.256 | FALSE |
The corresponding data of isIpv6 are as follows
IP | Expected |
---|---|
::1 | TRUE |
FF01::1101 | TRUE |
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