自动化测试用例排序(三个算法随机、贪心、额外贪心)
接着前面自动化测试的测试用例生成和收集,下面我们就需要做的是对测试用例进行排序,简单来说就是达到语句的全部覆盖。说到排序就涉及到三个算法,下面就是我对三个算法的阐述及代码实现。咱们由易入难。
1’自动用例生成(使用Randoop)>
2‘评价(对用例筛选冗余)>功能覆盖、语句覆盖(一般用后者)
>插桩 (插入语句)
用Javassist实现自动插入语句
3’测试用例排序排序>三种算法实现测试用例排序
1‘ 随机算法:测试用例会排序就是生成一个用例序列,随机算法就是随机生成一个测试用例序列
下面是对序列进行打乱随机排序的两种方法:
//(1)利用math的random方法
List l=new ArrayList( input); //将input数组放入arraylist,input为集合 List res=new ArrayList(); //用来存放随机产生元素的结果 Random r=new Random();//随机数 int size=l.size(); for(int i=0;i<size;i++){ res.add(l.remove( r.nextInt(l.size()))); //为了保证不重复,每次随机产生后都删除该元素。 } //此时res中存放的就是随机排序的结果。
//(2)利用collections的buffle方法
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class Test {
List list = new LinkedList();
public static void main(String[] args) {
List list = new LinkedList();
for ( int i = 0 ; i < 9 ; i ++ ) {
list.add( " a " + i);
}
Collections.sort(list); // 顺序排列
System.out.println(list);
Collections.shuffle(list); //随机打乱一个序列
System.out.println(list);
Collections.reverse(list); // 倒序排列
System.out.println(list);
System.out.println(Collections.binarySearch(list, " a5 " )); // 折半查找
}
对此我简单的用list输入一个测试用例序列并进行随机排序,输出结果:
package testpaixu;
import java.util.*;
public class randomA {
public static void main(String args[]){
//测试用例数目
int N=6;
List<Integer> lis=new ArrayList<Integer>();
//添加测试用例1,2,3,4,5,6为序列
for(int i=1;i<=N;i++){
lis.add(i);
}
//输出原测试用例序列
System.out.println("测试用例序列:"+lis);
//随机排序
Collections.shuffle(lis);
System.out.println("随机排序后测试用例序列:"+lis);
}
}
结果显示:

2’· 贪心算法:
一种能够得到某种度量意义下的最优解的分级处理方法,它总是做出在当前看来是最优的选择,也就是说贪心策略并不是从整体上加以考虑,它所做出的选择只是在某种意义上的局部最优解算法。
该算法存在问题:
1. 不能保证求得的最后解是最佳的;
2. 不能用来求最大或最小解问题;
3. 只能求满足某些约束条件的可行解的范围。
实现该算法的过程:
从问题的某一初始解出发;
while 能朝给定总目标前进一步 do
求出可行解的一个解元素;
由所有解元素组合成问题的一个可行解;
现在我用一个简单的覆盖矩阵进行代码实现:
首先我在F:\\目录下创建了一个txt文件,文件名 greedyA.txt,内容如下:

(横向为四个测试用例,纵向为5个覆盖语句,0为未覆盖,1为已覆盖)
首先我们先整体上对排序的逻辑过程进行考量一下,先从F:\\greedyA.txt目录下读取文件,并置入数组,然后对每个测试用例进行相加,数字最大的为覆盖范围最大的,将此加入新的数组,并将该测试用例之和重置为-1,以此类推,直到序列排序完成。
下面进行代码实现:
package testpaixu;
import java.io.*;
import java.util.*;
//贪婪算法
public class greedyA {
public static void main(String args[]){
//test number
int tn=4;
String[] test=new String[tn];
//下标为测试用例-1,数字为测试用例覆盖范围
int[] sum=new int[tn];
for(int i=0;i<sum.length;i++){
sum[i]=0; //初始化为0
}
//读取测试用例覆盖矩阵
String filePath="F:\\greedyA.txt";
try {
String encoding="GBK";
File file=new File(filePath);
//判断文件是否存在
if(file.isFile() && file.exists()){
//考虑到编码格式
InputStreamReader read = new InputStreamReader(
new FileInputStream(file),encoding);
BufferedReader br = new BufferedReader(read);
String lineTxt = null;
System.out.println("测试用例覆盖矩阵(横向测试用例,纵向覆盖语句(“0”未覆盖,“1”已覆盖)):");
while((lineTxt =br.readLine()) != null){
//分解覆盖矩阵
test=lineTxt.split(" ");
System.out.println(test[0]+test[1]+test[2]+test[3]);
//每个测试用例的覆盖范围
sum[0]=sum[0]+Integer.parseInt(test[0]);
sum[1]=sum[1]+Integer.parseInt(test[1]);
sum[2]=sum[2]+Integer.parseInt(test[2]);
sum[3]=sum[3]+Integer.parseInt(test[3]);
}
read.close();
}else{
System.out.println("找不到指定的文件");
}
} catch (Exception e) {
System.out.println("读取文件内容出错");
e.printStackTrace();
}
System.out.println("测试用例1覆盖范围:"+sum[0]);
System.out.println("测试用例2覆盖范围:"+sum[1]);
System.out.println("测试用例3覆盖范围:"+sum[2]);
System.out.println("测试用例4覆盖范围:"+sum[3]);
//使用贪婪算法对测试用例集排序
List<Integer> ls=new ArrayList<Integer>(); //初始化贪婪算法后序列
int[] tests=new int[tn];
for(int i=0;i<tests.length;i++){
tests[i]=i+1; //测试用例集初始序列
}
int index=0;
int max=sum[0];
for(int j=0;j<sum.length;j++){
//获取数组最大
for(int i=0;i<sum.length;i++){
if(sum[i]>max){
max=sum[i];
index=i;
}
}
ls.add(tests[index]);
sum[index]=-1;
max=sum[index];
}
System.out.println("测试用例集排序后序列: "+ls);
}
}
运行结果:

3‘ 额外贪心:迭代的选取覆盖实体最多的实例,然后对于剩下的测试用例调整覆盖信息,把被选取的测试用例标记为“覆盖的”,重复此过程直至测试实体集中所有的实例都被标记为“覆盖的”。
算法实现思想:对测试用例集的覆盖与未覆盖标记为“1”和“0”,获取测试用例集原始序列,对各测试的分布进行相加,结果最大的编入结果序列list2。起始设定两个序列list,一个全部为0记为slst,一个全部为1记为slst2。在求和获得最大的测试进行修改,将此项为1的全部编入slst;然后将slst中为1的对应项对所有测试用例集的对应项修改,将他们全部改为0(以便循环相加求和时不会冲突),以此循环直到slst==slst2为true。
到此,还有一步完善,将结果序列与原始序列相比较,先前未排序的加入到结果序列末端。
下面进行编译实现,同第二种算法一样,先于extragreedyA.txt中添加一个覆盖矩阵(横向为测试用例序列,纵向为覆盖语句序列),覆盖矩阵如下:

代码实现如下:
package testpaixu;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.*;
//额外贪心算法
public class extragreedyA {
public static void main(String args[]){
//test number
int tn=4;
//覆盖语句数量
int sn=5;
String[] test=new String[tn];
//下标为测试用例-1,数字为测试用例覆盖范围
int[] sum=new int[tn];
for(int i=0;i<sum.length;i++){
sum[i]=0; //初始化为0
}
//测试用例的覆盖语句分布
ArrayList<Integer> lst1=new ArrayList<Integer>();
ArrayList<Integer> lst2=new ArrayList<Integer>();
ArrayList<Integer> lst3=new ArrayList<Integer>();
ArrayList<Integer> lst4=new ArrayList<Integer>();
List<ArrayList<Integer>> lst=new ArrayList<ArrayList<Integer>>();
lst.add(lst1);
lst.add(lst2);
lst.add(lst3);
lst.add(lst4);
//初始化覆盖均为0,及均未覆盖
ArrayList<Integer> slst=new ArrayList<Integer>();
for(int i=0;i<sn;i++){
slst.add(0); }
System.out.println("初始化语句均为未覆盖:"+slst);
//最后覆盖语句均为1
ArrayList<Integer> slst2=new ArrayList<Integer>();
for(int i=0;i<sn;i++){
slst2.add(1); }
//读取测试用例覆盖矩阵
String filePath="F:\\extragreedyA.txt";
try {
String encoding="GBK";
File file=new File(filePath);
//判断文件是否存在
if(file.isFile() && file.exists()){
//考虑到编码格式
InputStreamReader read = new InputStreamReader(
new FileInputStream(file),encoding);
BufferedReader br = new BufferedReader(read);
String lineTxt = null;
System.out.println("测试用例覆盖矩阵(横向测试用例,纵向覆盖语句(“0”未覆盖,“1”已覆盖)):");
while((lineTxt =br.readLine()) != null){
//分解覆盖矩阵
test=lineTxt.split(" ");
System.out.println(test[0]+test[1]+test[2]+test[3]);
//每个测试用例的覆盖范围
sum[0]=sum[0]+Integer.parseInt(test[0]);
sum[1]=sum[1]+Integer.parseInt(test[1]);
sum[2]=sum[2]+Integer.parseInt(test[2]);
sum[3]=sum[3]+Integer.parseInt(test[3]);
//每个测试用例覆盖语句分布
lst.get(0).add(Integer.parseInt(test[0]));
lst.get(1).add(Integer.parseInt(test[1]));
lst.get(2).add(Integer.parseInt(test[2]));
lst.get(3).add(Integer.parseInt(test[3]));
}
read.close();
}else{
System.out.println("找不到指定的文件");
}
} catch (Exception e) {
System.out.println("读取文件内容出错");
e.printStackTrace();
}
//测试用例覆盖矩阵
for(int i=0;i<tn;i++){
System.out.println("测试用例"+(i+1)+"覆盖范围:"+sum[i]+"分布:"+lst.get(i));
}
//使用额外贪婪算法对测试用例集排序
ArrayList<Integer> ls=new ArrayList<Integer>(); //初始化贪婪算法后序列
int[] lstsum;
int add;
int index;
int max;
boolean b=true;
while(b){
//获得每个测试用例的覆盖范围
add=0;
lstsum=new int[tn];
for(int m=0;m<lst.size();m++){
for(int n=0;n<lst.get(m).size();n++){
int j=(Integer)lst.get(m).get(n);
add+=j;
}
lstsum[m]=add;
add=0;
}
//获取覆盖范围最大的测试用例
index=0;
max=lstsum[0];
//获取数组最大
for(int i=0;i<lstsum.length;i++){
if(lstsum[i]>max){
max=lstsum[i];
index=i;
}
}
ls.add((index+1));
//将初始序列覆盖范围改掉
for(int i=0;i<slst.size();i++){
if(lst.get(index).get(i)==1){
slst.set(i, 1);
}
}
//将该序列覆盖的均改为0,并延至所有序列
for(int i=0;i<lst.size();i++){
for(int j=0;j<lst.get(i).size();j++){
if(slst.get(j)==1){
lst.get(i).set(j, 0);
}
}
}
max=-1;
for(int i=0;i<slst.size();i++){
if(slst.get(i)!=slst2.get(i)){
b=true;break;
}
b=false;
}
}
//如果排序后还有测试用例没有排序,要对tests[]的剩余进行添加
ArrayList<Integer> tests=new ArrayList<Integer>();
for(int i=0;i<tn;i++){
tests.add(i+1);
}
System.out.println("测试用例集排序前序列: "+tests);
for(int i=0;i<tests.size();i++){
if(!(ls.contains(tests.get(i)))){
ls.add(tests.get(i));
}
}
System.out.println("测试用例集排序后序列: "+ls);
}
}
显示结果:

总结:
自动化测试的整体步骤如下(目标为一个程序a.java):
1‘先是利用randoop工具对程序自动生成测试用例,得到测试用例集文件(tests.java文件),并撰写生成报告(包含日期时间,原程序名称,测试数量及概述,操作时间等);
2’其次,对源程序进行插桩(利用Javassist进行方法,语句插桩)得到文件(a.class,与原a.class文件不同,目录不在bin文件夹内);
3’利用Java Runtime.exec()方法对测试用例集文件进行编译得到tests.class文件。
(该方法使用方法博客网址: http://www.cnblogs.com/mingforyou/p/3551199.html )
运行tests.class查看每个测试用例插桩结果:
a 获取覆盖信息(覆盖哪些语句,方法)并导入一个txt文件中方便查看、
b 各个用例的覆盖率(方便用贪心算法和随机算法)、
c 各个用例的覆盖分布(覆盖于未覆盖),已覆盖矩阵的形式导入txt文件中,以便运用额外贪心算法进行排序。
并撰写收集报告(包含日期时间,源程序名称,测试用例集名称,以及该集合的覆盖率矩阵,操作时间等);
4‘排序(三种算法),读取上一个步骤形成的两个txt文件,对覆盖率或者覆盖分布矩阵进行排序;
5’收集排序结果,并撰写排序报告(包含日期时间,源程序名称,测试用例集名称,排序实现程序名称,测试用例集的覆盖分布,测试用例集的结果序列等)。
题外话,过此我每个流程都进行了粗糙的代码实现,因此会对一个完整的程序进行完整的流程实现(自动生成测试用例,收集覆盖矩阵,排序,排序结果输出)。
浙公网安备 33010602011771号