軟件質量與測試第二週做業WordCount小程序的實現及測試
github地址
https://github.com/Asfalas/WordCounthtml
PSP
PSP2.1 | PSP 階段 | 預估耗時 (分鐘) | 實際耗時 (分鐘) |
---|---|---|---|
Planning | 計劃 | 20 | 15 |
· Estimate | · 估計這個任務須要多少時間 | 20 | 15 |
Development | 開發 | 300 | 345 |
· Analysis | · 需求分析 (包括學習新技術) | 20 | 15 |
· Design Spec | · 生成設計文檔 | 0 | 0 |
· Design Review | · 設計複審 (和同事審覈設計文檔) | 0 | 0 |
· Coding Standard | · 代碼規範 (爲目前的開發制定合適的規範) | 10 | 10 |
· Design | · 具體設計 | 30 | 20 |
· Coding | · 具體編碼 | 200 | 240 |
· Code Review | · 代碼複審 | 10 | 10 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 30 | 50 |
Reporting | 報告 | 100 | 100 |
· Test Report | · 測試報告 | 10 | 10 |
· Size Measurement | · 計算工做量 | 10 | 5 |
· Postmortem & Process Improvement Plan | · 過後總結, 並提出過程改進計劃 | 80 | 85 |
合計 | 420 | 460 |
解題思路
審題之後大體思路以下:java
一、資料能夠經過諸如百度、谷歌等搜索引擎獲取git
二、程序能夠分爲:命令參數分析、輸入輸出文件定位、執行命令三個主體部分github
(1)首先是命令參數分析部分,直觀的想法就是使用一個Vector<String>容器(Java Vector操做參見文後參考文獻)存儲命令參數,如:-c -w -l -s -a,另外後面需附加文件路徑的兩個命令:-o -e則直接進行分析處理再也不放入容器。正則表達式
(2)輸入文件定位,若是命令參數容器中含有-s,則執行findFiles命令,遞歸(參見參考文獻)查找該目錄下全部的符號要求的文件並放入全局容器Vector<File> sourcefile中,不然直接將輸入的單個文件放入容器中,至於輸出文件則直接複製給字符型變量outpath。小程序
(3)命令執行模塊中,實現創建類WordCount,它的屬性用來存放一寫統計用的變量數值,它的方法即對應每一個命令參數的不一樣功能,構造方法中須要輸入輸入文件容器,輸出文件名,停用詞表文件名用以傳遞參數。執行count操做時,將源文件依次按行讀入字符串s,對於-c命令,直接統計每一行的s.length的加和便可;對於-w命令,可以使用String類的split方法使用正則表達式對改行進行分割,按要求分割出單詞並統計數量。對於-l命令,每存在一行則將行數加一便可。app
(4)在主函數中一次對命令參數進行分析,定位輸入輸出文件並建立類WordCount的對象,最後調用該對象的方法進行響應的統計功能便可。函數
三、測試,即單元測試,在Test類中對上述的各類方法執行測試,主要採用的方法爲語句覆蓋測試。對每一個方法採用2-3個用例對其進行測試。單元測試
程序設計實現
本程序包含三個類:主函數類Main,統計類WordCount及測試類Test學習
主函數類以下圖所示:包括主方法main,命令解析方法analysisInstructions,遞歸文件查找方法findFiles以及停用詞表文件查找方法findStopList。
WordCount類中包括:各類屬性,構造方法WordCount,統計方法count,統計字符方法countChars,統計單詞方法countWords,統計行數方法countLines,初始化讀取文件方法initReadFile和釋放文件資源方法terminate,以下圖所示:
測試類詳見下面的測試設計過程
類及函數之間的調用流程以下:
在主方法中調用analysisInstructions解析命令並在此過程當中按照須要調用findFiles及findStopList初始化全局變量,並傳入這些全局變量以初始化WordCount的對象wc,調用wc的count方法進行對應的統計功能。
代碼說明
主函數:調用命令分析函數並初始化類WordCount的對象wc,再調用其count方法。
1 public static void main(String[] args) { 2 analysisInstructions(args); 3 if (sourcefile.isEmpty()) { 4 System.out.println("請輸入要統計的文件路徑名"); 5 return; 6 } 7 try{ 8 WordCount wc = new WordCount(sourcefile, outpath, stoplist); 9 wc.count(instructions); 10 } 11 catch(IOException e){ 12 System.out.println("找不到輸入文件或目錄"); 13 return; 14 } 15 return; 16 }
命令分析函數:包含對args[]參數的各個元素進行檢查並執行對應的操做,變量使用全局變量的形式存儲,所以函數返回值爲void。
1 public static void analysisInstructions(String[] args) { 2 File directory = new File(""); 3 path = directory.getAbsolutePath()+"\\";//獲取絕對路徑 4 instructions = new Vector<String>(); 5 stoplist = new Vector<String>(); 6 sourcefile = new Vector<File>(); 7 outpath = "result.txt"; 8 boolean flag = false; 9 for (int i = 0; i < args.length; i++) { 10 if (args[i].equals("-o")) { 11 if (++i == args.length) { 12 System.out.println("請輸入輸出文件路徑名"); 13 return; 14 } 15 outpath = args[i]; 16 } 17 else if (args[i].equals("-c") || args[i].equals("-w") || args[i].equals("-l")|| args[i].equals("-a")) 18 instructions.addElement(args[i]); 19 else if (args[i].equals("-e")) { 20 if (++i == args.length) { 21 System.out.println("請輸入停用文件路徑名"); 22 return; 23 } 24 try {findStopList(args[i]);} 25 catch (IOException e){ 26 System.out.println("找不到停用詞文件"); 27 return; 28 } 29 } 30 else if (args[i].equals("-s")) 31 flag = true; 32 else { 33 if (!flag) 34 sourcefile.addElement(new File(args[i])); 35 else { 36 String filter = args[i].substring(args[i].indexOf(".")); 37 System.out.println(filter); 38 if(args[i].indexOf("*") == 0) 39 findFiles(path, filter); 40 else 41 findFiles(args[i].substring(0,args[i].indexOf("*")),filter); 42 } 43 } 44 } 45 }
WordCount類的count方法:流程爲依次讀取源文件,並逐個按照命令容器中存在的命令進行處理。
1 public void count(Vector<String> instructions) throws IOException { 2 for (int i = 0; i < sourcepath.size(); i++) { 3 initReadFile(i); 4 if (instructions.contains("-c")) 5 countChars(i); 6 if (instructions.contains("-w")) 7 countWords(i); 8 if (instructions.contains("-l")) 9 countLines(i); 10 if (instructions.contains("-a")) 11 countOther(i); 12 } 13 terminate(); 14 }
下面以countWords爲例展現統計源代碼:逐行讀入源文件,去除空行後,使用split方法對用空格,逗號,換行符分割的字符串進行分割,而後再判斷是否同停用詞表中的一致,再統計單詞數。
1 public String countWords(int index) throws IOException { 2 initReadFile(index); 3 String s; 4 wordNum = 0; 5 while ((s = br.readLine()) != null) { 6 if (Pattern.matches("\\s*", s)) 7 continue; 8 String[] words = s.split(" |,|\\t"); 9 for (String l : words) 10 if (!stoplist.contains(l.toLowerCase()) && !Pattern.matches("\\s*",l)) 11 wordNum++; 12 } 13 String result = sourcepath.get(index).getAbsolutePath().substring(path.length()) 14 + ", 單詞數: " + String.valueOf(wordNum) + "\r\n"; 15 byte[] data = result.getBytes(); 16 output.write(data); 17 return result; 18 }
其他統計方法思路相似,在此不一 一展現,詳情請在github中查看源代碼。
測試設計過程
在test類中編寫單元測試,對本項目中的主要函數方法進行單元測試,主要採起語句覆蓋測試方法。
測試1:測試analysisInstructions方法,輸入"-c -l -w -a testcase.c -o 1.txt"後主類中的全局變量是否符合預期。
1 //測試輸入參數的解析方法 2 public static boolean testAnalysisInstructions() { 3 //用例1:語句覆蓋測試 4 String[] test = {"-c", "-l", "-w", "-a", "testcase.c", "-o", "1.txt"}; 5 Main.analysisInstructions(test); 6 if (Main.sourcefile.size() == 1 && Main.sourcefile.get(0).getName().equals("testcase.c")) 7 if (Main.stoplist.isEmpty()) 8 if (Main.outpath.equals("1.txt")) 9 if (Main.instructions.contains("-c") && 10 Main.instructions.contains("-w") && 11 Main.instructions.contains("-l") && 12 Main.instructions.contains("-a")) 13 return true; 14 return false; 15 }
測試二、三、4:對findStopList方法進行測試,針對三種狀況進行測試,覆蓋邊界,具體參見代碼註釋。stopList文件中含有{「apple」,「banana」,「pie」}三個停用詞。
1 public static boolean testFindStopList() { 2 try { 3 //用例2:stoplist文件中含有字符 4 Main.stoplist = new Vector<String>(); 5 Main.findStopList("testcase\\stoplist.txt"); 6 if (!(Main.stoplist.contains("apple") && Main.stoplist.contains("banana") 7 && Main.stoplist.contains("pie"))) 8 return false; 9 //用例3:stoplist文件爲空 10 Main.stoplist = new Vector<String>(); 11 Main.findStopList("testcase\\stoplist1.txt"); 12 if (!Main.stoplist.isEmpty()) 13 return false; 14 //用例4:stoplist文件中僅含有空格等格式控制字符 15 Main.stoplist = new Vector<String>(); 16 Main.findStopList(("testcase\\stoplist2.txt")); 17 if(!Main.stoplist.isEmpty()) 18 return false; 19 } catch (IOException E) { 20 return false; 21 } 22 return true; 23 }
測試五、六、7:測試主類的findFiles方法,針對路徑下含有遞歸的文件夾,不含有遞歸的文件夾以及路徑中無符合要求的文件進行測試,即語句覆蓋。
1 //測試查找文件方法() 2 public static boolean testFindFiles() { 3 //用例5:路徑下無遞歸文件夾 4 Main.sourcefile = new Vector<>(); 5 Main.findFiles("./testcase/test", ".c"); 6 if (!(Main.sourcefile.size() == 1 && Main.sourcefile.get(0).getName().equals("testcase1.c"))) 7 return false; 8 9 //用例6:路徑下有遞歸文件夾 10 Main.sourcefile = new Vector<>(); 11 Main.findFiles("./", ".c"); 12 for (File f : Main.sourcefile) { 13 if (f.getName().equals("testcase.c") || f.getName().equals("test.c")|| 14 f.getName().equals("testcase1.c") || f.getName().equals("testcase2.c")) 15 continue; 16 else 17 return false; 18 } 19 20 //用例7:無符合要求文件測試 21 Main.sourcefile = new Vector<>(); 22 Main.findFiles("./", ".none"); 23 if (!Main.sourcefile.isEmpty()) 24 return false; 25 return true; 26 }
測試八、九、10:測試方法以下,輸入文件容器以及預期的標準輸出文件,則該方法會自動執行並將結果與輸入的標準對比,若相同則返回true,不然返回false。爲保證測試覆蓋度,默認命令容器中含有全部的命令參數。
1 public static boolean testCount(Vector<File> source, String standardresult) { 2 Vector<File> sourcefile = source; 3 String outpath = "1.txt"; 4 try { 5 Main.findStopList("./testcase/stoplist.txt"); 6 Vector<String> stoplist = Main.stoplist; 7 WordCount wc = new WordCount(sourcefile, outpath, stoplist); 8 String[] ins = {"-c", "-w", "-l", "-a"}; 9 Vector<String> instructions = new Vector<>(); 10 for (String i : ins) 11 instructions.addElement(i); 12 wc.count(instructions); 13 InputStreamReader isr = new InputStreamReader( 14 new FileInputStream(new File("./1.txt"))); 15 BufferedReader result = new BufferedReader(isr); 16 InputStreamReader isr1 = new InputStreamReader( 17 new FileInputStream(new File(standardresult))); 18 BufferedReader standard = new BufferedReader(isr1); 19 String std, rs; 20 while ((rs = result.readLine()) != null) { 21 if ((std = standard.readLine()) == null) 22 return false; 23 if (!rs.equals(std)) 24 return false; 25 } 26 if ((std = standard.readLine()) != null) 27 return false; 28 } catch (IOException e) { 29 return false; 30 } 31 return true; 32 } 33 }
對該測試的調用以下:
1 source.addElement(new File("testcase/testcase.c")); 2 System.out.println(testCount(source, "./testcase/testresult.txt"));//用例8:僅有一個文件統計測試 3 source.addElement(new File("testcase/test/testcase1.c")); 4 System.out.println(testCount(source, "./testcase/testresult1.txt"));//用例9:含有遞歸文件夾的統計測試 5 source.clear(); 6 source.addElement(new File("testcase/testcase2.c")); 7 System.out.println(testCount(source, "./testcase/testresult2.txt"));//用例10:空文件的統計測試
用例詳情以及標準輸出文件參見github項目路徑下的testcase文件夾。
測試結果以下:
其他測試採用的老師在新發布的博客的樣例,全部用例均能經過。測試的高風險點包括代碼中包含分支斷定及循環的位置,在測試中採用的語句覆蓋的方法覆蓋到了全部程序代碼語句,用以應對高風險點。
參考文獻
http://blog.csdn.net/ycy0706/article/details/45457311 java統計一個文件字符數,單詞數等的一個小示例
https://www.cnblogs.com/zhaoyan001/p/6077492.html java vector使用教程
https://www.cnblogs.com/StanLong/p/6414383.html java輸出文件到本地
http://blog.csdn.net/chenqk_123/article/details/49304469 java指定目錄下遞歸讀取文件