第二周作业:WordCount小程序的实现及测试

软件质量与测试第二周作业WordCount小程序的实现及测试

github地址

  https://github.com/Asfalas/WordCount

PSP

  

PSP2.1PSP 阶段预估耗时 (分钟)实际耗时 (分钟)
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

解题思路

  审题以后大致思路如下:

  1、资料可以通过诸如百度、谷歌等搜索引擎获取

  2、程序可以分为:命令参数分析、输入输出文件定位、执行命令三个主体部分

    (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命令,每存在一行则将行数加一即可。

    (4)在主函数中一次对命令参数进行分析,定位输入输出文件并创建类WordCount的对象,最后调用该对象的方法进行响应的统计功能即可。

  3、测试,即单元测试,在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     }

  测试2、3、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     }

  测试5、6、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     }

  测试8、9、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指定目录下递归读取文件

posted @ 2018-03-20 09:44  zhangfj  阅读(514)  评论(0编辑  收藏  举报