TestNG同时使用DataProvider和Parameters

时间:2023-01-31 18:00:38

TestNG @Test测试方法中同时使用DataProvider和Parameters

实践中经常会遇到需要在@Test方法中混合使用TestNG的@DataProvider和@Parameters的情形,比如,

  • 根据参数的不同使用不同的测试数据
  • 根据参数的不同执行不同的测试逻辑
  • 测试数据含有大量的相同字段,为了提高效率

TestNG @Test方法本身不提供这样的语法(到目前为止最新的版本TestNG 6.9.4都没有这样的功能),使用时只能在annotation中指定@Dataprovider或者@Parameters,而不能两者同时指定。

其实这个问题很好解决,秘诀就在ITestContext,ITestContext 是TestNG 测试的上下文,它里面包含很多信息,包括我们定义在TestNG配置文件中的参数,下面是ITestContext的Java doc,

public interface ITestContext
extends IAttributes
This class defines a test context which contains all the information for a given test run. An instance of this context is passed to the test listeners so they can query information about their environment.

使用ITestContex获取参数的方法如下,

public void runTest(ITestContext context ,String id, String name, String input, String expectedValue) {
String testId = context.getCurrentXmlTest().getParameter("test_id");

ITestContext可以用在@Before, @Test, @DataProvider中。下面是TestNG官方文档中关于ITesetContext使用的信息。

5.18.1 - Native dependency injection

TestNG lets you declare additional parameters in your methods. When this happens, TestNG will automatically fill these parameters with the right value. Dependency injection can be used in the following places:
Any @Before method or @Test method can declare a parameter of type ITestContext.
Any @AfterMethod method can declare a parameter of type ITestResult, which will reflect the result of the test method that was just run.
Any @Before and @After methods can declare a parameter of type XmlTest, which contain the current tag.
Any @BeforeMethod (and @AfterMethod) can declare a parameter of type java.lang.reflect.Method. This parameter will receive the test method that will be called once this @BeforeMethod finishes (or after the method as run for @AfterMethod).
Any @BeforeMethod can declare a parameter of type Object[]. This parameter will receive the list of parameters that are about to be fed to the upcoming test method, which could be either injected by TestNG, such as java.lang.reflect.Method or come from a @DataProvider.
Any @DataProvider can declare a parameter of type ITestContext or java.lang.reflect.Method. The latter parameter will receive the test method that is about to be invoked.

混合@DataProvider和@Parameters的用法如下,

@Test(dataProvider = "MyDataProvider", dataProviderClass = com.demo.autotest.Executor.MyDataProvider.class)
public void runTest(ITestContext context ,String id, String name, String input, String expectedValue) {
String testId = context.getCurrentXmlTest().getParameter("test_id");

完整的demo project 如下,这个Project读取Excel文件并且将每一行作为一个测试数据传给Test,Test方法中根据配置的参数使用相应的测试数据。

MyTester.java

package com.demo.autotest.Executor;

import java.util.Properties;
import java.io.FileInputStream;
import java.io.IOException;

import org.testng.annotations.*;
import org.testng.ITestContext;

/**
* @ClassName: MyDataProvider
* @Description: Demo the mixing of DataProvider and Parameters
* @author: qinjun
*/
public class MyTester {

/**
* @Title: runTest
* @Description:Run the test with DataProvider and Parameters
* @return: void
*/
@Test(dataProvider = "MyDataProvider", dataProviderClass = com.demo.autotest.Executor.MyDataProvider.class)
public void runTest(ITestContext context ,String id, String name, String input, String expected) {
String testId = context.getCurrentXmlTest().getParameter("test_id");
if (testId.equalsIgnoreCase(id)) {
System.out.println("test id:["+testId+"]");
System.out.println("Got one test data.");
System.out.println("id:["+id+"]");
System.out.println("name:["+name+"]");
System.out.println("input:["+input+"]");
System.out.println("expected:["+expectedValue+"]");
}
}
}

MyDataProvider.java

package com.demo.autotest.Executor;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;

import org.testng.annotations.DataProvider;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
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;

/**
* @ClassName: MyDataProvider
* @Description: Demo the mixing of DataProvider and Parameters
* @author: qinjun
*/
public class MyDataProvider {

/**
* @Title: runTest
* @Description:Prepare the test data from excel
* @return: Object[][]
*/
@DataProvider(name = "MyDataProvider")
public static Object[][] getTestData() {
String excelPath = "D:\\SAF\\SAF_myversionbackup_before_usexiaolong\\SAF\\testcase\\testcase.xlsx";
String excelSheet = "test1";
int rowIndex = 0;
int colIndex = 0;

Sheet naviSheet = getSheet(excelPath, excelSheet);
int iLastRowIndex = naviSheet.getLastRowNum();
Row row = naviSheet.getRow(0);
int lastColIndex = row.getLastCellNum();
Object excelData[][] = new Object[iLastRowIndex+1][lastColIndex];
for (rowIndex = 0; rowIndex <= iLastRowIndex; rowIndex++) {
for (colIndex = 0; colIndex < lastColIndex; colIndex++) {
Cell cell = getCell(naviSheet, rowIndex, colIndex);
String paramValue = getcellvalue(cell);
excelData[rowIndex][colIndex] = paramValue;
}
}
return excelData;
}

/**
* @Title: getSheet
* @Description: Get the sheet from Excel
* @return: Sheet
*/
private static Sheet getSheet(String path, String sheetname){
InputStream fs = null;
Sheet naviSheet = null;
try {
fs = new FileInputStream(path);
Workbook wb = WorkbookFactory.create(fs);
naviSheet = wb.getSheet(sheetname);
}catch(IOException e)
{
e.printStackTrace();
}catch(InvalidFormatException e)
{
e.printStackTrace();
}
finally {
try {
fs.close();
} catch (IOException e) {
e.printStackTrace();
}
}

return naviSheet;
}

/**
* @Title: getSheet
* @Description: Get the Cell from Excel
* @return: Cell
*/
private static Cell getCell(Sheet sheet, int rowIndex, int columnIndex) {
Row row = sheet.getRow(rowIndex);
if (row == null) {
row = sheet.createRow(rowIndex);
}
Cell cell = row.getCell(columnIndex);
if (cell == null) {
cell = row.createCell((short) columnIndex);
}
return cell;
}

/**
* @Title: getCellValue
* @Description: Get the Cell value from Excel
* @return: String
*/
private static String getCellValue(Cell cell) {
String arg = "";
DecimalFormat df = new DecimalFormat("#");
switch (cell.getCellType()) {
case Cell.CELL_TYPE_STRING:
arg = (cell == null ? "" : cell.getStringCellValue());
break;
case Cell.CELL_TYPE_NUMERIC:
Double dvalue = (cell == null ? 0 : cell.getNumericCellValue());
arg = String.valueOf(dvalue);
if(arg.matches("\\d+.[0]*"))
{
int endIndex = arg.indexOf(".");
arg = arg.substring(0, endIndex);
}
if(arg.matches("^((-?\\d+.?\\d*)[Ee]{1}(\\d+))$"))
{
arg = df.format(dvalue);
}
break;
case Cell.CELL_TYPE_BOOLEAN:
Boolean bool = (cell == null ? false : cell.getBooleanCellValue());
arg = bool.toString();
break;
case Cell.CELL_TYPE_FORMULA:
arg = (cell == null ? "" : cell.getCellFormula());
break;
case Cell.CELL_TYPE_ERROR:
arg = (cell == null ? "" : Byte.toString(cell.getErrorCellValue()));
break;
case Cell.CELL_TYPE_BLANK:
arg = "";
break;
}
return arg;
}

}

TestNG配置文件,

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="suite1" verbose="2">
<parameter name="test_id" value="2"/>
<test name="test1">
<classes>
<class name="com.demo.autotest.Executor.MyTester" />
</classes>
</test>
</suite>

Excel 数据源

ID Name Input Expectation
1 name1 input1 expected1
2 name2 input2 expected2
3 name3 input3 expected3

参考文档
TestNG官方文档