接着前面自動化測試的測試用例生成和收集,下面我們就需要做的是對測試用例進行排序,簡單來說就是達到語句的全部覆蓋。說到排序就涉及到三個算法,下面就是我對三個算法的闡述及代碼實現。咱們由易入難。
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’收集排序結果,並撰寫排序報告(包含日期時間,源程序名稱,測試用例集名稱,排序實現程序名稱,測試用例集的覆蓋分布,測試用例集的結果序列等)。
題外話,過此我每個流程都進行了粗糙的代碼實現,因此會對一個完整的程序進行完整的流程實現(自動生成測試用例,收集覆蓋矩陣,排序,排序結果輸出)。
