这个作业属于哪个课程 | https://edu.cnblogs.com/campus/fzu/SE2020 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/fzu/SE2020/homework/11277 |
这个作业的目标 | 结对编程,学习前端,单元测试,github |
学号 | 031802513-031802516 |
团队成员
学号 | 姓名 |
---|---|
031802513 | 黄展 |
031802516 | 兰杰 |
1、开头:
标题 | 链接 |
---|---|
结对同学博客链接 | https://www.cnblogs.com/jieblue/ |
本作业博客的连接 | https://www.cnblogs.com/qewpqewp |
仓库地址 | https://github.com/jieblue/031802513-031802516 |
2、具体分工:
031802513完成js部分和vue的框架构建,单元测试框架
031802516完成html部分,完善单元测试用例,美化页面
3、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 70 |
Estimate | 估计这个任务需要多少时间 | 5 | 5 |
Development | 开发 | 300 | 350 |
Analysis | 需求分析 (包括学习新技术) | 50 | 100 |
Design Spec | 生成设计文档 | 25 | 30 |
Design Review | 设计复审 | 15 | 30 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 20 |
Design | 具体设计 | 50 | 80 |
Coding | 具体编码 | 25 | 70 |
Code Review | 代码复审 | 50 | 60 |
Test | 测试(自我测试,修改代码,提交修改) | 30 | 350 |
Reporting | 报告 | 25 | 50 |
Test Report | 测试报告 | 10 | 20 |
Size Measurement | 计算工作量 | 5 | 20 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 5 | 100 |
合计 | 665 | 1355 |
4、解题思路描述与设计实现说明
-
实现思路
0、此项目用Vue+element-ui实现,测试模块为jest+vue/test-utils
1、将文本处理,生成树结构
2、树结构:{name:"张三",children:[{name:"2018级研究生",children:[{name:"李四",detail:"java"},{name:"王五",}]},{name:"2019级研究生",children:[{name:"赵六"},{name:"陈7"}]}]}
(每个节点有name、detail、children字段)
3、树合并算法,遍历每个树的叶节点,判断是否有某根节点的name=叶节点的name,如果有,就将此根节点替换成此叶节点
4、将此树结构传入Tree组件,Tree组件递归形成图形树 -
数据流图
-
代码片段
树结构生成算法:将输入的文本逐行读取,每行出现“导师:”时,储存此导师名字,此时进入学员录入模式,之后的每一行都是该导师学员的信息。当读取到空行时,结束此导师的录入模式,重复此过程直到读取到某非空行且不处于学员录入模式时,则开始录入每个学员的具体信息。
initData() {
var nowTeacher = "";//当前教师
this.lines = this.text.split("\n");//以行分割
this.lines.forEach((item) => {//遍历每一行
if (item.includes("导师:")) {//判断是导师
var temp = item.split(":");
nowTeacher = temp[1];//开始录入此导师的子节点的标志
this.teacher.push({ name: temp[1], children: [], extend: false ,image_url : require("../assets/head.jpg")});
} else if (item === "") {//导师后出现空行
nowTeacher = "";//停止导师录入子节点
} else if (nowTeacher !== "") {//导师的子节点
var temp1 = item.split(":");
var students = temp1[1].split("、");
this.teacher.filter((item) => {
if (item.name === nowTeacher) {
var children = [];
students.forEach((s) => {
children.push({ name: s , image_url : require("../assets/head.jpg")});
});
item.children.push({
name: temp1[0],
children: children,
extend: false,
});
}
});
} else {//判断是学生的具体信息
var temp2 = item.split(":");//temp2[0]学生姓名,temp2[1]学生详情
this.teacher.forEach((teacher) => {
if (teacher.name === temp2[0]) {
teacher.detail = temp2[1];
}
teacher.children.forEach((grade) => {
grade.children.forEach((item) => {
if (item.name === temp2[0]) {
item.detail = temp2[1];
return;
}
});
});
});
}
});
},
},
五、附加特点设计与展示
设计的创意独到之处,这个设计的意义:
- 拖拽功能、结点收缩展开、文件上传
- 意义:当成员信息较多时,拖拽和结点收缩展开可以使得信息查看更便利,不会占据太多空间,文件上传可能没啥意义,后期可改成数据库读取
实现思路:
- 1、拖拽:当模块触发鼠标左键按下事件后,监听鼠标移动事件,将模块的位置加上鼠标偏移量,当鼠标左键抬起后停止监听。
- 2、结点收缩展开,由于树结点是递归形成,每个结点的树结构含有一个extend字段,当此字段为true时,此结点显示,反之隐藏
- 3、文件上传(略)
代码(拖拽功能):
directives: {
//拖拽功能
drag: {
// 指令的定义
bind: function (el) {
let oDiv = el; // 获取当前元素
oDiv.onmousedown = (e) => {
// 算出鼠标相对元素的位置
let disX = e.clientX - oDiv.offsetLeft;
let disY = e.clientY - oDiv.offsetTop;
document.onmousemove = (e) => {
// 用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
let left = e.clientX - disX;
let top = e.clientY - disY;
oDiv.style.left = left + "px";
oDiv.style.top = top + "px";
};
document.onmouseup = () => {
document.onmousemove = null;
document.onmouseup = null;
};
};
},
},
},
使用方法:在标签里面加个 -drag <div v-drag>
成果展示
六、在博客中给出目录说明和使用说明
目录组织:
```
├── public index.html默认位置
├── src
│ ├── assets 资源文件夹
│ ├── components 组件
│ ├── router 路由配置
│ └── views 主页面
└── test
└── unit 单元测试文件
```
运行方式:
- 1 安装node.js https://nodejs.org/zh-cn/ (12.9.0)
- 2 下载完可检查在windows任务命令行里输入node -v
- 3 使用淘宝NPM镜像源下载比较快 命令:npm install -g cnpm --registry=https://registry.npm.taobao.org
- 4 clone仓库
- 5 cd 031802513-031802516
- 6 npm install 安装框架
- 7 npm run serve 运行
- 8 访问 http://localhost:8080/
七、单元测试
-
测试工具:jest+vue/test-utils
-
安装方式:在构建vue项目的时候引入jest,之后在项目中npm install @vue/test-utils 安装vue/test-utils模块
-
在tests/unit文件夹内创建 xx.spec.js文件,用来写测试用例
简易教程(测试用例文件写法): 1、import { shallowMount } from '@vue/test-utils' //引入 vue/test-utils的 shallowMount函数来挂载需要测试的组件 2、例子
import Vue from 'vue'
import index from '@/components/index'
import { shallowMount } from '@vue/test-utils'
describe('index页面测试', () => {
//shallowMount挂载测试组件(此函数不生成子组件,mount生成子组件)
const wrapper = shallowMount(index)
//断言格式
it('验证是否渲染了页面按钮', () => {
expect(button.text()).toEqual('生成')
})
3、 同等性断言 Equality Asserts
expect(sth).toEqual(value)
expect(sth).not.toEqual(value)
比较性断言 Comparison Asserts
expect(sth).toBeGreaterThan(number)
expect(sth).toBeLessThanOrEqual(number)
类型性断言 Type Asserts
expect(sth).toBeInstanceOf(Class)
条件性测试 Condition Test
expect(sth).toBeTruthy()
expect(sth).toBeFalsy()
expect(sth).toBeDefined()
4、运行npm run test:unit
- 单元测试代码
index.vue 测试
const button = wrapper.findAllComponents({ name: 'el-button' }).at(1)
it('验证是否渲染了页面按钮', () => {
expect(button.text()).toEqual('生成')
})
wrapper.vm.text = "导师:张三"
button.trigger('click');
it('测试按钮是否能点击', () => {
expect(wrapper.vm.teacher[0].name).toEqual("张三")
})
it('测试树生成算法', () => {
wrapper.vm.text = `
导师:张三
2016级博士生:天一、王二、吴五
2015级硕士生:李四、王五、许六
2016级硕士生:刘一、李二、李三
2017级本科生:刘六、琪七、司四
刘六:JAVA、数学建模
李二:字节跳动、京东云
`
button.trigger('click');
var teacher = wrapper.vm.teacher[0]
expect(teacher.children[0].name).toEqual("2016级博士生")
expect(teacher.children[1].children[2].name).toEqual("许六")
expect(teacher.children[3].children[0].detail).toEqual("JAVA、数学建模")
})
it('测试复杂树生成算法,当一个导师是另一个导师的学员的情况', () => {
wrapper.vm.text = `
导师:张三
2016级博士生:天一、王二、吴五
2015级硕士生:李四、王五、许六
2016级硕士生:刘一、李二、李三
2017级本科生:刘六、琪七、司四
导师:王九
2013级博士生:刘久、张三、周七
2012级硕士生:李一、王八、许四
刘六:JAVA、数学建模
李二:字节跳动、京东云
`
button.trigger('click');
var teacher = wrapper.vm.teacher[0]
expect(teacher.children[0].name).toEqual("2013级博士生")
expect(teacher.children[0].children[1].name).toEqual("张三")
expect(teacher.children[0].children[1].children[1].children[2].name).toEqual("许六")
})
tree.vue测试
it('验证树根组件是否正确渲染', () => {
expect(wrapper.find('.name').text()).toEqual('张三')
})
it('验证树结点数量', () => {
expect(wrapper.findAllComponents(tree).length).toBe(7)
})
it('验证子节点组件是否正确渲染', () => {
expect(wrapper.findAllComponents(tree).at(1).findAllComponents(tree).at(1).find(".name").text()).toEqual('李四')
})
const button=wrapper.findComponent({name:'el-button'})
it('验证学生查看详情按钮', () => {
expect(button.text()).toEqual('查看详情')
wrapper.vm.showDetail({name:"李四",detail:"haha"})
expect(wrapper.vm.detail).toEqual("haha");
})
- 测试数据思路,先测试单棵树的情况,就类似给出的测试数据,然后测试当一个导师是另一个导师的学员的情况,最终结果的树会合并。测试人员可能会输入一个不符合命名标准的数据,此代码并没有判断错误命名的能力。
8、github 签入记录
9、遇到的代码模块异常或结对困难及解决方法
最大的困难是单元测试模块的引入。刚开始引入的时候,所有的测试用例所指向的组件都出现js代码错误,最开始的报错是"forEach"函数不存在。经多次查找资料,发现是因为jest版本过低,js编码不支持es6版本,而forEach是es6新引入的特性。
于是手动修改package.json升级模块,但由于涉及到的模块特别多,每个模块的版本又都互相限制,因此会产生各种不支持。
后来尝试了重新构建项目,引入Karma+Mocha模块单元测试,但与vue/test-utils不太兼容,对于element-ui无法测试。
最终将vue-cli升级到vue-cli 3版本,并重构项目,才得以成功进行单元测试。
期间非常绝望,花了两天时间来解决这个问题,重构项目好几次,几乎快要放弃了,甚至去询问唯一认识会vue的学姐,结果告诉我她不会写测试。
收获:还是趁早放弃为好,下次可没有那么幸运了。不能丢了西瓜捡芝麻。
10、评价队友
* 值得学习的地方:期间一直劝我不要放弃,要坚持
* 需要改进的地方:页面设计得不好看