最近公司在推进研发自测的功能,首先第一点就是要提高测试覆盖率,只有测试覆盖率上去了才能尽可能的找出隐藏的问题和bug。所以就有了一个如何最方便高效的提高测试覆盖率的问题。
测试覆盖率检测工具有很多常用的idea就有这个功能,原理也很简单就是跑一遍测试然后记录这些测试跑过的代码有哪些,没跑过的有哪些然后计算一个比例。
想法也就按照这个思路来,就是构造尽可能全的场景参数,然后调用待测试方法。
下面的代码就是一个构造参数笛卡尔积的方法,这个方法主要功能就是把一个参数类里的所有的参数的可能性构造出多个参数,不包含过滤功能。
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
/**
* @description: 动态构建参数的笛卡尔积
* @author:
* @date:
*/
public class ClassUtil {
/**
* 循环实现dimValue中的笛卡尔积,结果放在result中
* @param claz 创建类
* @param dimValue 原始数据
* @param maxNum 生成实例最值,默认1000
* @return result 结果数据
*/
public static List<Object> buildObjects(Class claz, Map<String, List<Object>> dimValue, int maxNum) {
if (maxNum > 1000 || maxNum <= 0) {
maxNum = 1000;
}
int total = 1;
for (String s : dimValue.keySet()) {
total *= dimValue.get(s).size();
}
//创建所有的bean
List<DynamicBean> beanMapList = new ArrayList<>();
for (int i = 0; i < total; i++) {
try {
Class<?> aClass = Class.forName(claz.getName());
Object o = aClass.newInstance();
beanMapList.add(new DynamicBean(o));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
int itemLoopNum;
int loopPerItem;
int now = 1;
Iterator<Map.Entry<String, List<Object>>> iterator = dimValue.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, List<Object>> next = iterator.next();
List<Object> list = next.getValue();
now *= list.size();
int index = 0;
int currentSize = list.size();
itemLoopNum = total / now;
loopPerItem = total / (itemLoopNum * currentSize);
int myIndex = 0;
for (Object object : list) {
for (int i = 0; i < loopPerItem; i++) {
if (myIndex == list.size()) {
myIndex = 0;
}
for (int j = 0; j < itemLoopNum; j++) {
DynamicBean dynamicBean = beanMapList.get(index);
dynamicBean.setValue(next.getKey(), list.get(myIndex));
index++;
}
myIndex++;
}
}
}
List<Object> result = new ArrayList<>();
for (int i = 0; i < beanMapList.size(); i++) {
if (i < maxNum) {
result.add(beanMapList.get(i).getObject());
} else {
break;
}
}
return result;
}
}
构建所有的参数笛卡尔积的过程中需要用到动态代理构造对象,并给对象的属性赋值
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.beans.BeanGenerator;
import org.springframework.cglib.beans.BeanMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* @description: 动态bean创建
* @author:
* @date:
* */
@Slf4j
public class DynamicBean {
private Object object = null; //动态生成的类
private BeanMap beanMap = null; //存放属性名称以及属性的类型
public DynamicBean() {
super();
}
public DynamicBean(Map propertyMap) {
this.object = generateBean(propertyMap);
this.beanMap = BeanMap.create(this.object);
}
public DynamicBean(Object o) {
this.object = o;
this.beanMap = BeanMap.create(o);
}
/**
* @param propertyMap
* @return
*/
private Object generateBean(Map propertyMap) {
BeanGenerator generator = new BeanGenerator();
Set keySet = propertyMap.keySet();
for (Iterator i = keySet.iterator(); i.hasNext(); ) {
String key = (String) i.next();
generator.addProperty(key, (Class) propertyMap.get(key));
}
return generator.create();
}
/**
* 给bean属性赋值
*
* @param property 属性名
* @param value 值
*/
public void setValue(Object property, Object value) {
try {
beanMap.put(property, value);
} catch (Exception e) {
log.info("set value error! property: {}, value: {}", property.toString(), JSONObject.toJSONString(value), e);
throw new RuntimeException();
}
}
/**
* 通过属性名得到属性值
*
* @param property 属性名
* @return 值
*/
public Object getValue(String property) {
return beanMap.get(property);
}
/**
* 得到该实体bean对象
*
* @return
*/
public Object getObject() {
return this.object;
}
}
这是自己在解决这个问题中摸索出来的一种办法,其实还不完善,还需要很多优化的地方,比如需要加上过滤无效参数组合的功能,对参数有效性的检查等等。