介绍
在TestNG中,一个强大的功能是参数测试。在大多数情况下,你会遇到这样一个场景,业务逻辑需要一个巨大的不同数量的测试。参数测试,允许开发人员运行同样的测试,一遍又一遍使用不同的值。
TestNG让你直接传递参数测试方法两种不同的方式:
testng.xml配置参数:在testng.xml文件中定义的简单参数,然后在源文件中引用这些参数
DataProvider注解: 当你需要通过复杂的参数或参数需要创建从Java(复杂的对象,对象读取属性文件或数据库等..),在这种情况下,可以将参数传递使用数据提供者。数据提供者@DataProvider的批注的方法。这个注解只有一个字符串属性:它的名字。如果不提供名称,数据提供者的名称会自动默认方法的名称。数据提供者返回一个对象数组。
而本文介绍在Spring中集成TestNG测试框架,是以CSV外部输入参数为参数,通过DataProvider注解生成TestNG测试所需外部参数:
- 相关源码已经在集成在我的github项目AdminEAP中。
说明
文件结构
运行结果
代码示例
在BaseTest类中,含有DataProvider注解的方法,该数据提供者方法,按照测试类名和测试方法名找到外部csv的文件,并把文件中的数据参数化,提供给集成了BaseTest的类
- Maven中引入外部依赖
<!--spring-test-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.0.8.RELEASE</version>
</dependency>
<!--csv reader-->
<dependency>
<groupId>net.sf.supercsv</groupId>
<artifactId>super-csv</artifactId>
<version>2.4.0</version>
</dependency>
<!--testng-->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.9.10</version>
</dependency>
- 数据提供者父类 BaseTest.java
package com.billJiang.framework.testng;
import com.billJiang.framework.base.pojo.CsvPOJO;
import com.billJiang.framework.base.pojo.CsvRow;
import com.billJiang.framework.utils.CsvUtil;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.testng.ITestContext;
import org.testng.annotations.DataProvider;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.billJiang.framework.utils.DateUtil;
/**
* Created by billJiang on 2016/10/22.
*/
@ContextConfiguration(locations = { "classpath:spring-hibernate.xml","classpath:spring.xml" })
@TransactionConfiguration(defaultRollback = true)
public class BaseTest extends AbstractTestNGSpringContextTests {
@DataProvider
public Object[][] dataProvider(ITestContext context, Method method) {
CsvPOJO csvPojo = CsvUtil.getCaseList(this.getClass().getSimpleName() + "_" + method.getName() + ".csv");
Class<?>[] types = method.getParameterTypes();
int param_len = types.length;
int size = csvPojo.getRows().size();
Object[][] result = new Object[size][];
for (int i = 0; i < csvPojo.getRows().size(); i++) {
CsvRow row = csvPojo.getRows().get(i);
List<String> objList = row.getCols();
int obj_len = objList.size();
if (param_len != obj_len) {
throw new RuntimeException("CSV参数个数与测试方法" + method.getName() + "参数个数不一致");
}
// 类型转换
List<Object> obj_list = new ArrayList<Object>();
for (int j = 0; j < obj_len; j++) {
if (types[j] == String.class) {
obj_list.add(objList.get(j));
} else if (types[j] == Integer.class) {
obj_list.add(Integer.valueOf(objList.get(j)));
} else if (types[j] == Double.class) {
obj_list.add(Double.valueOf(objList.get(j)));
} else if (types[j] == Float.class) {
obj_list.add(Float.valueOf(objList.get(j)));
} else if (types[j] == Date.class) {
obj_list.add(DateUtil.parseToDate(objList.get(j)));
} else if (types[j] == Boolean.class) {
obj_list.add(Boolean.valueOf(objList.get(j)));
// TO DO 各种扩展
}
}
result[i] = obj_list.toArray();
}
return result;
}
}
- 读取csv参数文件工具类 CsvUtil.java
package com.billJiang.framework.utils;
import com.billJiang.framework.base.pojo.CsvPOJO;
import com.billJiang.framework.base.pojo.CsvRow;
import org.springframework.core.io.Resource;
import org.supercsv.io.CsvMapReader;
import org.supercsv.io.ICsvMapReader;
import org.supercsv.prefs.CsvPreference;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* billJiang http://blog.csdn.net/jrn1012
*/
public class CsvUtil {
public static CsvPOJO getCaseList(String fileName) {
CsvPOJO pojo = null;
Resource[] resources = ConfigurationUtil.getAllResources("/**/" + fileName);
if (resources == null || resources.length == 0) {
throw new RuntimeException("CSV文件《" + fileName + "》没有找到!");
}
// for(Resource resouce:resources){
// if(fileName.equals(resouce.getFilename())){
try {
pojo = readWithCsvMapReader(resources[0].getFile().getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
// }
// }
return pojo;
}
private static CsvPOJO readWithCsvMapReader(String file) throws Exception {
CsvPOJO csvPojo = new CsvPOJO();
Map<String, String> readMap = null;
ICsvMapReader mapReader = null;
try {
mapReader = new CsvMapReader(new FileReader(file), CsvPreference.STANDARD_PREFERENCE);
String[] headers = mapReader.getHeader(true);
List<CsvRow> rows = new ArrayList<CsvRow>();
while ((readMap = mapReader.read(headers)) != null) {
CsvRow row = new CsvRow();
List<String> columns = new ArrayList<String>();
for (String h : headers) {
if (!StrUtil.isEmpty(h)) {
columns.add(readMap.get(h));
}
}
row.setCols(columns);
rows.add(row);
}
csvPojo.setHeaders(headers);
csvPojo.setRows(rows);
} finally {
if (mapReader != null) {
mapReader.close();
}
}
return csvPojo;
}
}
- csv数据接口 CsvPOJO.java
package com.billJiang.framework.base.pojo;
import java.util.List;
public class CsvPOJO {
private String[] headers;
private List<CsvRow> rows;
public String[] getHeaders() {
return headers;
}
public void setHeaders(String[] headers) {
this.headers = headers;
}
public List<CsvRow> getRows() {
return rows;
}
public void setRows(List<CsvRow> rows) {
this.rows = rows;
}
}
- csv数据接口 CsvRow.java
package com.billJiang.framework.base.pojo;
import java.util.List;
public class CsvRow {
private List<String> cols;
public List<String> getCols() {
return cols;
}
public void setCols(List<String> cols) {
this.cols = cols;
}
}
- 测试类 DemoTest.java
package com.test.demo;
import com.billJiang.framework.testng.BaseTest;
import org.testng.Assert;
import org.testng.annotations.Test;
public class DemoTest extends BaseTest {
@Test(dataProvider = "dataProvider", groups = { "function-test" })
public void testArea(String caseId, Double w, String l, String h, String check) {
Assert.assertEquals(w * Double.parseDouble(l) + Double.parseDouble(h), Double.parseDouble(check));
}
}
- 参数文件 DemoTest_testArea.csv
id,length,width,height,result
001,1,2,3,5
002,1,2,4,6
003,1,2,5,7
004,1,2,6,5
- 测试类 UserTest.java
package com.test.demo;
import com.cnpc.framework.base.entity.User;
import com.cnpc.framework.base.service.UserService;
import com.cnpc.framework.testng.BaseTest;
import org.testng.Assert;
import org.testng.annotations.Test;
import javax.annotation.Resource;
public class UserTest extends BaseTest {
@Resource
private UserService userService;
@Test(dataProvider = "dataProvider", groups = { "function-test" })
public void checkUserExist(String code, String loginname) {
System.out.println(code + "-----" + loginname);
User user = userService.getUserByLoginName(loginname);
Assert.assertNotNull(user);
}
}
- 参数文件 UserTest_checkUserExist.csv
id,loginName
001,jrn
002,tester
004,qqqfr
- 配置文件 RootSuit.xml
<?xml version="1.0" encoding="UTF-8"?>
<suite name="Suite" parallel="false">
<test name="DataProviderTest">
<groups>
<run>
<include name="function-test"/>
<exclude name="master-test"/>
<exclude name="unit-test"/>
</run>
</groups>
<classes>
<class name="com.test.demo.DemoTest" group="function-test"/>
<class name="com.test.demo.UserTest" group="function-test"/>
</classes>
</test>
</suite>
右键RootSuit.xml可运行测试,并在控制台输出测试结果,也可在DemoTest或者UserTest分别运行