142
社区成员




这个作业属于哪个课程 | 2022年福大-软件工程;软件工程实践-W班 |
---|---|
这个作业要求在哪里 | 软件工程实践第二次作业——个人实战 |
这个作业的目标 | 读写文件 网页爬取 json解析 cmd运行 单元测试 性能测试与改进 |
其他参考文献 | Maven镜像(下载jar包用的) |
本次作业的爬虫行为仅用于教学,并无恶意
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
• Estimate | • 估计这个任务需要多少时间 | 10 | 10 |
Development | 开发 | ||
• Analysis | • 需求分析 (包括学习新技术) | 180 | 240 |
• Design Spec | • 生成设计文档 | 30 | 20 |
• Design Review | • 设计复审 | 20 | 20 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 20 | 30 |
• Design | • 具体设计 | 30 | 30 |
• Coding | • 具体编码 | 300 | 420 |
• Code Review | • 代码复审 | 30 | 180 |
• Test | • 测试(自我测试,修改代码,提交修改) | 30 | 120 |
Reporting | 报告 | ||
• Test Repor | • 测试报告 | 20 | 30 |
• Size Measurement | • 计算工作量 | 10 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 20 | 20 |
合计 | 700 | 1130 |
本次作业实际用时与预期不符主要在于需求的变化导致分析时间增长、编码时间的增长,除此之外代码复审和测试也是比预期高,主要原因在于自己是第一次接触实际开发的测试。
自己平时都是使用idea运行java项目,这次突然说要cmd就能运行,原以为就在命令台中使用cd跳到对应的目录下再使用命令java+java文件名+参数启动入口类或者使用编译+运行指令就能运行,结果遇到了不少的问题。
最主要的问题还是下面这种情况:
在同学和助教的帮助下也知道了解决方法:
1、首先将项目生成为jar包。idea生成并导出jar包的方法
2、找到自己项目的jar包位置。
3、用指令运行jar包。
最终效果如图:
首先想到的是split截取每一行的指令,再对每一行再分别用split进行截取每一个空格隔开的字符,然后对各类可能的输出进行分类判断。
1、无输出,跳过
2、输出奖牌榜
3、输出对应日期的赛程
4、输出N/A
5、输出Error这里的区分需要
(1)先去冬奥网址查看可以发起请求的地址,copy过来后再对条件判断修改实现自定义条件
去官网后按f12或者右键--->‘检查’;然后点击‘网络’,找到对应的地址
(2)再调用请求的方法获取Json数据
(3)再对Json数据进行解析(需要导包)即可
(1)数据模块
通过data软件包存放数据模块,目前仅含Information接口,便于数据(如网页爬取的地址)的存放与重复使用的。
(2)功能模块
Lib类,用于存放各个单一功能的函数
(3)对象模块
通过models软件包存放各个类对象,目前含CountryInfo类和MatchInfor类,方便Json解析后对数据的输入输出
(1)入口类OlympicSearch
函数 | 功能 |
---|---|
executeInput | 处理输入指令跳转到对应接口 |
(2)功能类Lib
函数 | 功能 |
---|---|
getTotalContent | 根据json数据获取返回total数据 |
getScheduleContent | 根据json数据获取返回schedule数据 |
getNetTotal | 获取网址内部数据并封装返回json对应的String数据 |
readFile、writeFile | 读取、写入文件 |
isRightDate | 判断字符串是否为0202~0215 |
(3)对象类
|函数| 功能|
|--|--|
| CountryInfo | 参数为Json对象,获取json对象内部中自己需要的属性|
|getCountryInfos|根据要求格式获取内容|
情况 | 处理 |
---|---|
空行 | 无输出,跳过 |
1、total | 写入奖牌榜 |
2、schedule+有效日期 | 写入对应日期的赛程 |
3、schedule+无效日期 schedule+...(...代表两个及以上) | 写入N/A |
4、其他 | 写入Error |
public static ArrayList<String> executeInput(String inputCmd, String toFile) {
ArrayList<String> cmdList=new ArrayList<>();//有效的指令
String[] cmds=inputCmd.split("\n");//获取cmd指令
Lib.writeFile("",toFile,false);//文件清空
for (String oneCmd:cmds) {
String[] oneCmdInfo=oneCmd.split(" ");
//判断指令、执行指令
if (oneCmdInfo.length==1) {
if (oneCmdInfo[0].equals("total")) { //情况1:total,输出
...
}
else if (oneCmdInfo[0].equals("schedule")){//情况3: N/A
...
}
else if (!oneCmdInfo[0].equals("")) //非法,Error
...
}
else
if (oneCmdInfo[0].equals("schedule")) {
if (!Lib.isRightDate(oneCmdInfo[1])||oneCmdInfo.length!=2) //情况2:schedule ****不存在
...
else {//情况3:schedule ****存在
...
}
}
else ...//非法,Error
}
return cmdList;
}
//对有效指令进行处理
public static void dealWithCmd(ArrayList<String> cmdList,String toFile){
for (int i=0;i<cmdList.size();i++){
String writeData,rightCmd=cmdList.get(i);
if (dataMap.containsKey(rightCmd)) writeData=dataMap.get(rightCmd);
else {
if (rightCmd.equals("total")) {
String jsoncontent=Lib.getNetResponse(TOTAL_ADDRESS);
writeData=Lib.getTotalContent(jsoncontent);
dataMap.put(rightCmd,writeData);
}else if(rightCmd.equals("Error"))
writeData=ERROR_INFO;
else if(rightCmd.equals("NA"))
writeData=NA_INFO;
else {
String jsoncontent=Lib.getNetResponse(SCHEDULE_ADDRESS.replace("mmdd",rightCmd));
System.out.println(rightCmd);
writeData=Lib.getScheduleContent(jsoncontent);
dataMap.put(rightCmd,writeData);
}
}
if (i==cmdList.size()-1)
Lib.writeFile(writeData,toFile,true);
else Lib.writeFile(writeData+"\n",toFile,true);
}
}
情况 | 处理 |
---|---|
错误地址 | 输出“Error:Something wrong with function getNetResponse().” |
返回码非200 | 输出“The request has been rejected.” |
返回码200 | 处理返回值后返回对应的json字符串 |
public static String getNetResponse(String address) {
...
try {
//发送请求
URL url=new URL(address);
HttpURLConnection httpUrl=(HttpURLConnection)url.openConnection();
//System.out.println(httpUrl.getResponseCode());
if (httpUrl.getResponseCode()==200){
//获取流
InputStream is=httpUrl.getInputStream();
//utf-8字符转换
BufferedReader br=new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
String line;
while ((line=br.readLine())!= null)
sb.append(line);
//结束、关闭
is.close();
br.close();
//处理返回值
...
...
}else {
System.out.println("The request has been rejected.");
}
}catch (IOException e){
e.printStackTrace();
}
//处理返回值
...
...
影响性能因素 | 改进方法 |
---|---|
每次获取信息都要向网站发起一次请求,速度较慢 | 创建一个Map,每次获取指令对应内容的时候判断是否存过对应数据,若有直接读取,若无则发起请求获取数据再存入Map |
使用JProfiler进行性能分析
原方法:
运行一万次的两个指令(total和schedule 0220)运行了2分54秒左右
使用map存放已经获取过的数据改进后:
只运行了13秒左右,不过有一点不剩很清楚的就是内存占用高了一点点而已,可能是因为原来占用率本就高的问题??
带着疑惑检查了下,发现map占用内存比确实不高(但不懂为什么...)
个人感觉本次作业覆盖率不高的原因有以下:
1、写入无法执行到的语句
2、测试的时候没测全面
其中测试没测全主要是网页请求没测到200返回码之外的情况:
由于一开始不会伪造网页请求200之外的返回值,导致有些语句无法执行,选择上图中的if...else...判断删除直接执行if内部语句即可达到100%(但是这样不太好)
后来经过不断测试后发现这样可以返回404添加了这个地址的测试
添加了新测试后完成单元测试
覆盖率(第二张是因为测试的时候发现有新要求后又修改了点代码):
测试结果:
异常 | 处理 |
---|---|
主函数参数个数错误 | 输出"Error:Something wrong with args." |
读取错误文件 | 输出"Error:Something wrong with function readFile()." |
写入错误文件 | 输出"Error:Something wrong with function writeFile()." |
网络请求返回码非200 | "The request has been rejected." |
网络请求地址无效 | 输出"Error:Something wrong with function getNetResponse()." |
在程序设计和实现的时候往往不是一个要求执行到底,本次作业就出现了需求的不断完善的过程,
实实在在体会到了什么是乙方也在对照需求的过程中不断修正自己的代码 。
第一次接触单元测试,也通过这个单元测试修正了不少的bug
第一次接触性能测试,以前写程序代码的时候很少考虑性能,考虑性能的时候一般是写算法题的时候
本次作业顺便巩固了下java的相关知识(小组前端人员,后端最近比较少学习)