在日常的测试工作中,从理论上来说,一个接口方法可以用一个用例就搞定测试,然后传入不同的参数的不同组合,在用例方法里做不同的分支判断校验。看似很美好,包罗万象的参数组合,实现其来很麻烦,且有如下缺点:
- 且需要花大量的精力去判断分支路径,并做不同的断言,破坏了方法的原子性,不要指望一个方法干所有事情,我们一直强调模块化编程。
- 脚本存在大量的if-else分支,for循环等,脚本可读性差 维护成本高。
- 测试没有侧重点。
那么,有什么好的方法来避免这个问题呢?首先,我们要明白为什么要做数据驱动,数据驱动的原则是什么?这个问题,相同字段,输入不同的数据,通过调取被测代码,获取不同的测试结果。每组数据都是相同的字段,进一步可以把一组参数抽象为一个对象,这一组参数组合就是这个对象的不同元素。我们要做的工作就是不断创建这样的对象,并传递给测试方法。那么数据驱动怎么做呢?有哪些数据驱动方式呢?一般来说,主要有以下几种方式传入数据:
- 以硬编码的方式写在代码里,简单直观,但代码和数据未分离,不方便以后维护。
- 从文件读取数据,如csv,excel、txt、xml等格式文件。不用修改测试逻辑,只需要维护测试数据。
- 直接调用接口获取数据源。
- 本地封装一些生成数据的方法。
以上几种方式生成的数据,在@DataProvider方法中以Object二位数组返回数据,测试用例可以直接获取。通过@datapovider方法数据驱动有以下几点优点:
1.像在循环里一样,自动遍历所有数据组合
2.某一组数据执行测试方法失败,不会影响其他数据组合继续执行。
3.测试代码不用加过多的数据判断,要的只是对被测代码的数据输出和结果断言。
例如要测试一个列表查询的接口,有很多查询条件,那么需要测试,默认查询、单一条件查询、组合条件查询等场景,其实就是设置不同的查询参数,其余测试步骤及校验大抵相同。
1.单一条件查询,遍历所有查询条件,传入测试方法,数据的获取需要封装成合适的方法,可读性好,例如这里buildQueryParmGroup方法就是封装构造查询参数的方法
private static Map, String> buildQueryParmGroup() {
HashMap, String> map = new HashMap, String>();
map.put("id", "1003266");
map.put("type", "1");
map.put("venderId", "20032");
map.put("businessId", "100073920");
map.put("title", "砍价活动api测试");
map.put("skuId", "1200039810");
map.put("oneCategory", "1315");
map.put("twoCategory", "1342");
map.put("threeCategory", "9733");
map.put("beginTime", "2017-01-04 16:19:45");
map.put("endTime", "2017-02-03 16:19:45");
map.put("status", "1");
map.put("pin", "test_sop01");
map.put("customerPin", "sop_order_new");
return map;
}
@DataProvider(name = "queryParamProperties")
public static Object[][] queryParamProperties() {
Map, String> queryParmGroup = buildQueryParmGroup();
Set, String>> entrySet = queryParmGroup.entrySet();
Object[][] objects = new Object[entrySet.size()][];
Object[] objects1 = entrySet.toArray();
for (int i = 0; i < objects.length; i++) {
objects[i] = new Object[]{objects1[i]};
}
return objects;
}
@Test(dataProvider = "queryParamProperties")
@Description(description = "单一条件查询砍价列表")
public void testGetByBargainList_2(Map.Entry, String> entry) throws Exception {
// ConvertUtils.register(new DateLocaleConverter(), Date.class);
ConvertUtils.register(new Converter() {
public Object convert(Class type, Object value) {
if (value == null) {
return null;
}
if (!(value instanceof String)) {
throw new ConversionException("传入的不是字符串");
}
if (((String) value).trim().equals("")) {
return null;
}
DateFormat df = new SimpleDateFormat(PromoConstant.YYYYMMDDHHMMSS);
try {
return df.parse((String) value);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}, Date.class);
BargainQueryParm queryParam = new BargainQueryParm();
BeanUtils.setProperty(queryParam, entry.getKey(), entry.getValue());
Result bargainList = this.invoke(bargainReadService, "getBargainList", List.class, clientInfo, queryParam);
Assert.assertTrue(bargainList.isSuccess());
}
组合条件查询,随机生成参数组合,查询条件放到一个map里,封装一个buildQueryParmSubMap方法用于随机生成查询条件组合,返回一个map<条件参数名,条件参数值>
private static Map, String> buildQueryParmSubMap(Map, String> map, int size) {
Set, String>> entries = map.entrySet();
ImmutableSet, String>> subSet = ImmutableSet.copyOf(Iterables.limit(entries, size));
Map, String> subMap = new HashMap, String>();
Iterator, String>> iterator = subSet.iterator();
while (iterator.hasNext()) {
Map.Entry, String> entry = iterator.next();
subMap.put(entry.getKey(), entry.getValue());
}
return subMap;
}
数据驱动方法:
@DataProvider(name = "queryParmGroup")
public static Object[][] queryParmGroup() {
Map, String> queryParmGroup = buildQueryParmGroup();
Object[][] objects = new Object[100][];
for (int i = 0; i < objects.length; i++) {
objects[i] = new Object[]{buildQueryParmSubMap(queryParmGroup, RandomUtils.nextInt(queryParmGroup.size()))};
}
return objects;
}
@Test(dataProvider = "queryParmGroup")
@Description(description = "组合条件查询砍价列表")
public void testGetByBargainList_3(Map, String> map) throws Exception {
ConvertUtils.register(new Converter() {
public Object convert(Class type, Object value) {
if (value == null) {
return null;
}
if (!(value instanceof String)) {
throw new ConversionException("传入的不是字符串");
}
if (((String) value).trim().equals("")) {
return null;
}
DateFormat df = new SimpleDateFormat(PromoConstant.YYYYMMDDHHMMSS);
try {
return df.parse((String) value);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}, Date.class);
BargainQueryParm queryParam = new BargainQueryParm();
BeanUtils.populate(queryParam, map);
Result getBargainList = this.invoke(bargainReadService, "getBargainList", List.class, clientInfo, queryParam);
Assert.assertTrue(getBargainList.isSuccess());
}