在日常的测试工作中,从理论上来说,一个接口方法可以用一个用例就搞定测试,然后传入不同的参数的不同组合,在用例方法里做不同的分支判断校验。看似很美好,包罗万象的参数组合,实现其来很麻烦,且有如下缺点:
- 且需要花大量的精力去判断分支路径,并做不同的断言,破坏了方法的原子性,不要指望一个方法干所有事情,我们一直强调模块化编程。
- 脚本存在大量的if-else分支,for循环等,脚本可读性差 维护成本高。
- 测试没有侧重点。
那么,有什么好的方法来避免这个问题呢?首先,我们要明白为什么要做数据驱动,数据驱动的原则是什么?这个问题,相同字段,输入不同的数据,通过调取被测代码,获取不同的测试结果。每组数据都是相同的字段,进一步可以把一组参数抽象为一个对象,这一组参数组合就是这个对象的不同元素。我们要做的工作就是不断创建这样的对象,并传递给测试方法。那么数据驱动怎么做呢?有哪些数据驱动方式呢?一般来说,主要有以下几种方式传入数据:
- 以硬编码的方式写在代码里,简单直观,但代码和数据未分离,不方便以后维护。
- 从文件读取数据,如csv,excel、txt、xml等格式文件。不用修改测试逻辑,只需要维护测试数据。
- 直接调用接口获取数据源。
- 本地封装一些生成数据的方法。
以上几种方式生成的数据,在@DataProvider方法中以Object二位数组返回数据,测试用例可以直接获取。通过@datapovider方法数据驱动有以下几点优点:
1.像在循环里一样,自动遍历所有数据组合
2.某一组数据执行测试方法失败,不会影响其他数据组合继续执行。
3.测试代码不用加过多的数据判断,要的只是对被测代码的数据输出和结果断言。
例如要测试一个列表查询的接口,有很多查询条件,那么需要测试,默认查询、单一条件查询、组合条件查询等场景,其实就是设置不同的查询参数,其余测试步骤及校验大抵相同。
1.单一条件查询,遍历所有查询条件,传入测试方法,数据的获取需要封装成合适的方法,可读性好,例如这里buildQueryParmGroup方法就是封装构造查询参数的方法
private static Map<String, String> buildQueryParmGroup() { HashMap<String, String> map = new HashMap<String, 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, String> queryParmGroup = buildQueryParmGroup(); Set<Map.Entry<String, 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, 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<List> bargainList = this.invoke(bargainReadService, "getBargainList", List.class, clientInfo, queryParam); Assert.assertTrue(bargainList.isSuccess()); }组合条件查询,随机生成参数组合,查询条件放到一个map里,封装一个buildQueryParmSubMap方法用于随机生成查询条件组合,返回一个map<条件参数名,条件参数值>
private static Map<String, String> buildQueryParmSubMap(Map<String, String> map, int size) { Set<Map.Entry<String, String>> entries = map.entrySet(); ImmutableSet<Map.Entry<String, String>> subSet = ImmutableSet.copyOf(Iterables.limit(entries, size)); Map<String, String> subMap = new HashMap<String, String>(); Iterator<Map.Entry<String, String>> iterator = subSet.iterator(); while (iterator.hasNext()) { Map.Entry<String, String> entry = iterator.next(); subMap.put(entry.getKey(), entry.getValue()); } return subMap; }数据驱动方法:
@DataProvider(name = "queryParmGroup") public static Object[][] queryParmGroup() { Map<String, 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, 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<List> getBargainList = this.invoke(bargainReadService, "getBargainList", List.class, clientInfo, queryParam); Assert.assertTrue(getBargainList.isSuccess()); }