团队队员:201421123043 邱文鑫 + 201421123045 念其锋
码市的地址链接:https://git.coding.net/qwx_hh/Test.git
一、背景介绍
1.什么是单元测试?
在《构建之法》课本的第二章对于“单元测试”有如下的解释:绝大部分软件都是由多人合作完成的,大家的工作相互有依赖关系。最典型的例子就是,某人负责的模块的功能被其他人调用。软件的很多错误都来源于程序员对模块功能的误解、疏忽或不了解模块的变化。如何能让自己负责的模块功能定义尽量明确,模块内部的改变不会影响其他模块,而且模块的质量能得到稳定的、量化的保证呢?这时候就需要单元测试来解决这个问题。
2.为什么需要单元测试呢(需求分析)?
单元测试是由程序员自己来完成,最终受益的也是程序员自己。可以这么说,程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试。执行单元测试,就是为了证明这段代码的行为和我们期望的一致。
在一般情况下,一个功能模块往往会调用其他功能模块完成某项功能,如业务层的业务类可能会调用多个DAO完成某项业务。对某个功能模块进行单元测试时,我们希望屏蔽对外在功能模块的依赖,以便将焦点放在目标功能模块的测试上。这时模拟对象将是最有力的工具,它根据外在模块的接口模拟特定操作行为,这样单元测试就可以在假设关联模块正确工作的情况下验证本模块逻辑的正确性了。
单元测试的意义主要有以下几点:
· (1)减少bug
· 一个机器,由各种细小的零件组成,如果其中某件零件坏了,机器运行故障。必须保证每个零件都按设计图要求的规格,机器才能正常运行。一个可单元测试的工程,会把业务、功能分割成规模更小、有独立的逻辑部件,称为单元。单元测试的目标,就是保证各个单元的逻辑正确性。单元测试保障工程各个“零件”按“规格”(需求)执行,从而保证整个“机器”(项目)运行正确,最大限度减少bug。
· (2)快速定位bug,减少调试时间
· 如果程序有bug,我们运行一次全部单元测试,找到不通过的测试,可以很快地定位对应的执行代码。修复代码后,运行对应的单元测试;如还不通过,继续修改,运行测试.....直到测试通过。
对于Android项目,要测试某个功能点,不用单元测试的话,必须运行在真机、模拟器上,慢慢debug找到问题点。运行程序到真机,快则半分钟,慢则几分钟。junit只需在本地运行即可,就几秒的事(robolectric需要十几秒)。有时,写那个功能模块的员工已离职,APP运行出错(逻辑错误,非crash or exception),你根本就不知道调试哪个类。如果离职的员工之前写了单元测试,运行一下立马就找到问题点了。单元测试大大减少调试时间,从而达到节约时间成本的效果。
· (3)提高代码质量
· 由于每个单元有独立的逻辑,做单元测试时需要隔离外部依赖,确保这些依赖不影响验证逻辑。因为要把各种依赖分离,单元测试会促进工程进行组件拆分,整理工程依赖关系,更大程度减少代码耦合。这样写出来的代码,更好维护,更好扩展,从而提高代码质量。
· (4)放心重构
· 重构,每个开发者都会经历,重构后把代码改坏了的情况并不少见。以往,写完一个框架,运行APP,没什么问题,完事。由于最初的框架并不是你写的,可谓牵一发动全身,你改1个方法导致整个框架运行失败....
如果你有单元测试,情况大不相同。写完一个类,把单元测试写了,确保这个类逻辑正确;写第二个类,单元测试.....写100个类,道理一样,每个类做到第一点“保证逻辑正确性”,100个类拼在一起肯定不出问题。你大可以放心一边重构,一边运行APP;而不是整体重构完,提心跳胆地run。
3.怎样才算是一个好的单元测试呢?
· (1)单元测试一个在最基本的功能/参数上验证程序的正确性;
· (2)单元测试必须由最熟悉代码的人(程序的作者)来写;
· (3)单元测试后,机器状态保持不变;
· (4)单元测试要快(一个测试的运行时间是几秒钟,而不是几分钟);
· (5)单元测试应该产生可重复、一致的结果;
· (6)单元测试应该覆盖所有代码路径;
· (7)单元测试应该集成到自动测试的框架中;
· (8)单元测试必须和产品代码一起保护和维护。
二、设计测试框架,模拟测试数据
1.请给出计算模块的测试用例及运行结果
以下的这次计算模块我们写在同一个模块当中,但是测试的时候我们是分开测试的,具体如下:
(1)整数的除法测试:
(2)分数的除法测试:
(3)整数的加法测试
(4)分数的加法测试
(5)整数的减法测试
(6)分数的减法测试
(7)整数的乘法测试
(8)分数的乘法测试
(9)分数的覆盖率测试
(10)整数的覆盖率测试
2.描述测试过程中遇到的问题及解决方案
(1)对代码测试几乎不了解,在插件的安装上花费了很多时间,特别是代码覆盖率这一块。具体命令并不熟悉,花了时间研究了基础操作,基本上都是通过百度来获得解决的办法;
(2)原本计算的类包含了随机数选择、分数计算与整数计算这三个,所以刚刚知道实验要求的时候我们无从下手。后来经过我们的讨论之后,还是决定将原先的一个类拆分成三个类,然后把其中的分数计算类和整数计算类分别摘出来测试,这也给我们带来了较大的工作量;
(3)因为在原先的类中,取值已经避免了0当除数与分子的产生,所以没有必要测试这两种情况的异常报错。
(4)当我们在进行分数运算的时候,我们发现有的时候结果本应该显示成整数,但是程序运行的结果却是假分数,这不符合化简的要求,所以我们添加了几句代码,使得当分子与分子与分母的公约数相除等于1或者-1时,输出分母除分子,即从假分数转换成整数。
3.请展示上面描述的单元测试的每个环节
三、关于结对编程
1.使用结对编程模式的感受
这是我们第二次使用结对编程的合作模式,在我们还没有接触结对编程的时候,我们都认为两个人一起做一个项目,就应该是一个人负责实现这几种功能,另一个人负责实现另外几种功能,最后再将这些功能合并到一起,就形成我们俩一起合作完成的项目。有了第一次结对编程的经验,我们这次一开始就找到了自己“驾驶员”和“领航者”的定位,两个人同时坐在同一台电脑前,一个人敲代码另一个帮忙复审和提建议,监督一下有没有什么错误,一段时间后再对调角色,循环往复。这次结对编程与第一次结对编程的感觉不同,具体有以下几点:
(1)、当我作为“领航员”的时候,我就坐在我队友的旁边看着他敲代码,一有错误就能及时纠正他;
(2)、在结对编程期间两个人对同一段代码有不同的理解,可以有效地避免许多错误的产生,一个人的思考往往会局限于自己的思维跳不出自己的“死胡同”,正所谓当局者迷旁观者清;
(3)、我们两个人在结对编程的时候,还是遇到了一些我们俩都无法解决的问题,不过在度娘的帮助之下,攻克了一个又一个的技术屏障,在互联网上寻找符合自己要求的小段代码经过自己的改造在自己的程序上“开花结果”,由此可见,互联网拉近了我们与知识的距离。
2.给我队友的“汉堡包”
(1)、我的队友在Java方面的基础较差,但是根据老师给的步骤可以比较完整的写出测试代码。
(2)、在共同编写代码的时候,他能虚心接受我提出的建议加以改正,我相信他在一次次的编程实践中就能提高自我的编程水平。
3.照片
四、一周之后看代码
1.良好的设计
一开始的设计真的很重要,一开始的设计可以多花一点时间,这叫“磨刀不误砍柴工”,否则会出现很多的麻烦。比如一开始设计的时候,没有用字符串来封装式子,导致在主函数中,要对输入的字符串进行各种拆分各种判断,导致逻辑结构不清晰,代码冗余大。所以这一点的改进使得后续的一些函数编写容易多了。
2.编码规范
我们的编码规范在第一次结对编程的时候就定好了,在后面有添加变量的时候我们会立即跟对方通气,所以我们在一周之后再看代码的时候,只要对照着之前我们签订的代码规范文档来看,就可以很快地看懂一周前的那些代码,从而节省了很多时间。
3.必要的注释
注释这方面我们俩做得还是有点少,我们一般都会在宿舍直接向对方提出来,有的时候大家会听完就忘了,比如一周后往往记不起之前当面说的那些注释,所以把一些注释记录下来还是非常有必要的,也可以节省一些时间。
五、PSP
PSP2.1 |
personal sofware process stages |
time(%)senior student |
time(%) |
planning |
计划 |
10 |
15 |
estimeate |
估计这个任务需要多少时间 |
200 |
320 |
development |
开发 |
15 |
20 |
analysis |
需求分析 |
20 |
10 |
design spec |
生成设计文档 |
20 |
15 |
design review |
设计复审 |
15 |
30 |
coding standard |
代码规范 |
10 |
10 |
design |
具体设计 |
30 |
60 |
coding |
具体编码 |
100 |
200 |
code review |
代码复审 |
10 |
20 |
test |
测试 |
50 |
100 |
reporting |
报告 |
40 |
50 |
|
测试报告 |
15 |
10 |
|
计算工作量 |
5 |
10 |
|
并提出过程改正计划 |
10 |
5 |