软件质量与测试第二周做业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指定目录下递归读取文件