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