第四周小组作业wcPro
1 基本任务:代码编写+单元测试
1.1 小组github地址
1.2 PSP表格
PSP2.1 | PSP阶段 | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 60 |
Estimate | 估计任务需要多少时间 | 30 | 60 |
Development | 开发 | 180 | 230 |
Analysis | 需求分析 | 20 | 10 |
Design Spec | 生成设计文档 | 20 | 20 |
Design Review | 设计复审 | 20 | 20 |
Coding Standard | 代码规范 | 30 | 10 |
Design | 具体设计 | 30 | 30 |
Coding | 具体编码 | 30 | 60 |
Code Review | 代码复审 | 30 | 20 |
Test | 测试 | 50 | 60 |
Reporting | 报告 | 70 | 120 |
Test Report | 测试报告 | 30 | 40 |
Size Measurement | 计算工作量 | 20 | 40 |
Postmortem | 总结 | 20 | 40 |
合计 | 340 | 410 |
1.3 接口实现及具体编码
· 接口描述
本次小组任务中我所需要实现的模块是核心处理部分,接口是接受输入模块传过来的字符串进行分析,统计字符串中的所有单词及其词频,再将其放置在一个Map容器内输出(未经过排序)。
· 设计思路
使用一个类wcMainProcess实现相关功能,内有两个方法,分别为词频统计方法countWordFrequency()和单词判断函数word()。思路为使用递归下降的方法对字符串进行分析,设置一个属性为 index记录当前所读的字符位置,先预读一个字符,如果是字母a-z,则调用word方法检索以该字母开头的符合规则的单词,直到后面的字符不是a-z,如果是-,则再次预读一个字符,若为字母则递归调用word向后检索;如果不是-,则将检索的字符放入stringBuilder中并返回。
· 具体实现
(1)word()检索判断单词方法,fileContent为输入模块所返回的字符串,index为当前索引位置。
1 public StringBuilder Word() 2 { 3 char temp = fileContent.charAt(index); 4 while(temp >= 'a' && temp <= 'z') 5 { 6 curWordBuilder.append(temp); 7 if(index == fileContent.length()-1) 8 return curWordBuilder; 9 temp = fileContent.charAt(++index); 10 } 11 if(temp == '-') 12 { 13 if(index + 1 < fileContent.length()) 14 { 15 char nextChar = fileContent.charAt(index + 1); 16 if((nextChar >= 'a' && nextChar <= 'z')) 17 { 18 curWordBuilder.append('-'); 19 index++; 20 Word(); 21 } 22 } 23 } 24 return curWordBuilder; 25 }
(2)countWordFrequency(),使用word()方法依次检索fileContent里的每个单词并统计词频放入map容器,最后返回统计完成后的map容器
1 public Map<String,Integer> countWordFrequency() 2 { 3 curWordBuilder = new StringBuilder(); 4 for(;index < fileContent.length(); index++) 5 { 6 char temp = fileContent.charAt(index); 7 if(temp >= 'a' && temp <= 'z') 8 { 9 String word = Word().toString(); 10 if(result.containsKey(word)) 11 result.put(word,result.get(word)+1); 12 else 13 result.put(word,1); 14 curWordBuilder = new StringBuilder(); 15 } 16 } 17 return result; 18 }
1.4 测试用例设计
本次测试使用黑盒测试和白盒测试(条件判定覆盖)结合的方式,各选取10个用例对两个方法进行单元测试,具体用例设计参见测试用例清单。
以Word()方法为例,其中有5个条件和7个逻辑表达式判定,使用条件/判定覆盖方式进行测试时,用例将每种逻辑表达式均覆盖到,可按照如下清单方式选择测试用例。黑盒测试则是将单词分成若干等价类,每个等价类选取1-2个测试用例进行测试,如只有字母的单词,用-连接的单词,用其他特殊符号连接的单词等,详情见测试用例清单如下:
1.5 单元测试运行结果
使用JUnit4框架进行单元测试,在类wcMainProcessTest中对wcMainProcess进行单元测试。
为了测试的可重用性,我将用例并没有写死在代码中,而是写在了文件里,在@beforeClass注解的方法中将其读入用例的容器,提高了测试用例的可重用性和可修改性及独立性。
参数化测试方法
由于测试用例数目较多,我使用了单元测试的参数化方法,使用@RunWith(Parameterized.class)注解,同时构建了参数构造方法,如下图所示:
1 @Parameterized.Parameters 2 public static Collection testData(){ 3 return Arrays.asList(new Object[][] 4 { 5 { 0 }, 6 { 1 }, 7 { 2 }, 8 { 3 }, 9 { 4 }, 10 { 5 }, 11 { 6 }, 12 { 7 }, 13 { 8 }, 14 { 9 }, 15 { 10 }, 16 { 11 }, 17 { 12 }, 18 { 13 }, 19 { 14 }, 20 }); 21 }//将用例的序号传入下面的测试构造方法 22 23 //根据序号从初始化容器中取出用例 24 public wcMainProcessTest(int index){ 25 wcMainProcess wcmp = new wcMainProcess(usecasesOfWord.elementAt(index)); 26 actualOfWord = wcmp.Word().toString(); 27 expectOfWord = expectsOfWord.elementAt(index); 28 wcmp = new wcMainProcess(usecasesOfWordFrequency.elementAt(index)); 29 actualOfWordFrequency = wcmp.countWordFrequency(); 30 expectOfWordFrequency = expectsOfWordFrequency.elementAt(index); 31 32 }
单元测试方法如下:
1 /** 2 * 3 * Method: countWordFrequency() 4 * 5 */ 6 @Test 7 public void testCountWordFrequency() throws Exception { 8 assertEquals(expectOfWordFrequency, actualOfWordFrequency); 9 } 10 11 /** 12 * 13 * Method: Word() 14 * 15 */ 16 @Test 17 public void testWord() throws Exception 18 { 19 assertEquals(expectOfWord,actualOfWord); 20 }
测试结果截图如下:
测试质量和测试代码质量均较高,可以在9ms内跑完所有的30个用例。
1.6 小组贡献分
根据小组讨论结果,小组贡献分大致为 17046-0.27分、17043-0.26分、17053-0.25分、17049-0.22分。
2 扩展功能
2.1 开发文档的选择与说明
我们在开发过程中选择了《阿里巴巴Java开发手册》,通过老师在博客中发布的链接进行下载:https://yq.aliyun.com/attachment/download/?id=4942
并且可以了解开发手册对应插件在IDEA上的应用:https://yq.aliyun.com/articles/225187?spm=5176.10695662.1996646101.searchclickresult.6ff77f392uUgs1
2.2 同学的代码问题
我负责检查文宇凡(17053)的代码,可以看出,他的编码习惯还是比较好的,存在的问题就是类名的驼峰命名法、包名的大小写问题 ,缺少创建者信息,以及使用了行尾注释的问题。最为重要的一点就是test类方法较为冗余,建议采取参数化方法简化代码结构。
2.3 代码检查工具及说明
我们使用的是“alibaba java coding guidance”插件,可以通过idea直接下载使用,注意保持网络畅通(血的教训)。路径:setting-plugin-Browse Repositories-search,如下图所示:
2.4 我的代码存在的问题
我的代码在自己检测的过程中也存在不少的问题,包括一个我第一次了解到的“魔法值问题”,即出现了未定义的字符或字符串常量,应先用一个常量存放该字符常量等问题,同时还有一些注释的规范。还有值得一提的就是字符串判断相等,应改为("xxx").equals(String)的形式,这样可以避免出现错误,是个良好的编码习惯。
2.5 小组存在的问题
总而言之,我们小组的编码规范问题还是挺多的,我们没有使用UpperCamelCase风格作为类名,导致在修改某些类时,因为同样的方法被多次调用所以修改成本非常高,包名等命名规范问题注意之后就有很大改善,修改的并不多,不过仍需要注意;再就是在自己的代码上方添加自己的有关注释,这也是一件看起来十分职业规范化的一件事,综上,大致就是我们小组共同的问题。
3 高级功能
3.1 测试数据集设计思路
使用文件大小比较大的数据集来对本程序进行压力测试。我使用了英文的电子书籍《了不起的盖茨比》作为构造测试集,分别构造出了大小为500k,1m,2m,4m,8m,16m的数据集,同时使用了参数化方法对Main函数进行集成的多次测试,观察运行时间。
3.2 优化前性能指标
程序的大小以及对应的时间消耗
3.3 同行评审过程
小组成员:张付俊(U201517046)、孙帅(U201517043)、张瑞祺(U201517049)、文宇凡(U201517053)
人员角色分工:张付俊(作者、讲解员)、孙帅(作者,评审员)、张瑞祺(作者,主持人)、文宇凡(作者、评审员)
评审目的:确保要发布质量可靠的代码,发现各种类型的错误,提高代码质量、规范性、一致性和可维护性,提高代码效能。
评审意见:
孙帅:主要对于程序的代码规范角度与部分功能模块角度提出意见
文宇凡:主要对于代码的功能实现角度提出意见
评审结论:
孙帅:(1)代码注释部分较少。可读性较差。
(2)针对输出测试类方法代码存在大量冗余,需要改进。
文宇凡:
(1)将输入字符串传入核心处理模块导致两遍扫描文本,可能会使效率下降,可以一边读文本一边进行排序工作。
(2)代码规范性存在许多问题需要改进。
总体结论:代码需要作者做进一步改进
本次评审的不足:
(1)由于时间较为紧凑,部分细节的评审并未做到面面俱到。
(2)因为代码量较少,评审内容有限,不能发现作者的隐藏问题。
3.4 测试后得到的结论
通过测试可以发现,本程序可以在较短的时间内完成对500k,1m,2m,4m,8m,16m等大小的文件进行统计词频。
3.5 优化思路及其指标
优化思路如下:可以对一边对文本进行按字符读取,一边进行单词判断统计等功能,最后使用优化的随机快速排序算法对容器进行排序,因为时间有限,优化尚未完成。
3.6 总结说明
"通过基本任务、扩展任务、到高级任务的完成,如何体现软件开发、软件测试、软件质量之间的关系"
我认为软件开发、软件测试和软件质量这三者之间是存在紧密的联系的。
软件开发离不开测试。不仅是相互的测试,专业测试员对代码的测试,程序开发者也应对自己的代码进行测试,来保证软件的质量。
首先,程序员是最了解自己的代码逻辑的,因此很方便进行白盒测试,在开发过程中就可以找到自身的不足之处,但是由于程序猿写代码过程中的定式思维,许多隐藏的代码难以被发现,此时就需要测试工程师对代码进行单独测试,只有通过了完整和系统的软件测试的软件的质量才是有一定的保障的。
以上便是本次小组实验所得到的结论。
4 附加功能
使用-x参数可以调出图形界面进行选取文件,代码如下:
1 JFileChooser jfc=new JFileChooser("."); 2 int returnVal = jfc.showOpenDialog(null); 3 if(returnVal == JFileChooser.APPROVE_OPTION) 4 { 5 //获得打开的文件 6 inputFile = jfc.getSelectedFile().getAbsolutePath(); 7 }
5 参考文献
https://blog.csdn.net/casularm/article/details/164877(map的性质)
https://yq.aliyun.com/articles/225187?spm=5176.10695662.1996646101.searchclickresult.6ff77f392uUgs1(idea上使用代码规范插件)