Fork me on GitHub

自动化测试用例排序(三个算法随机、贪心、额外贪心)

接着前面自动化测试的测试用例生成和收集,下面我们就需要做的是对测试用例进行排序,简单来说就是达到语句的全部覆盖。说到排序就涉及到三个算法,下面就是我对三个算法的阐述及代码实现。咱们由易入难。

 

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’收集排序结果,并撰写排序报告(包含日期时间,源程序名称,测试用例集名称,排序实现程序名称,测试用例集的覆盖分布,测试用例集的结果序列等)。

   题外话,过此我每个流程都进行了粗糙的代码实现,因此会对一个完整的程序进行完整的流程实现(自动生成测试用例,收集覆盖矩阵,排序,排序结果输出)。

 

posted @ 2017-07-19 15:51  sunwengang  阅读(1711)  评论(0编辑  收藏