文章目录
碎碎念
因为第一篇由于一些原因的时间比较靠后,不过还是继续复习第二章吧,剩下的会在CSAPP考完试再赶一赶进度,真的太忙了。这篇博客借鉴了许多其他同学的博客,并且根据PPT和笔记体会写出来的,
尤其是这位大佬的博客写的真的好好,十分感谢,很通透,给大家指路:
大佬第二章博客
以下是本篇文章正文内容
一、Testing and Test-First Programming
第二章是·软件测试与测试优先的编程
1 Software Testing
1.1软件测试是提高软件质量的重要手段
- 软件测试是一个以找bug为目的的过程,并且检验该软件是否达到可用级别(用户需求)。
- 软件测试只关注某一侧面的特性,比如该软件主要执行的功能是什么,我们就要集中精力在这个主要功能上,让它尽可能避免出错。
注意:
- 软件测试是提高软件质量的重要手段,但并非唯一、决定性手段,软件测试只能提高正确性。
- 考试常考:“好的测试能够证明程序是正确的”这句话是错误的,因为测试是为了找BUG
1.2 即使是最好的测试,也可能出错
这里需要用**残留缺陷率(defects/kloc)**来进行评判:每一千行代码出现的错误个数。其评判标准如下:
- 1-10 defects/kloc: Typical industry software.(典型行业软件)
- 0.1-1 defects/kloc: High-quality validation. The Java libraries might achieve this level of correctness.
- 0.01-0.1 defects/kloc: The very best, safety-critical validation. NASA and companies like Praxis can achieve this level.
可以看到即使是再好的测试,也不能达到百分之一百无错误,也无法证明系统里不存在错误。
1.3什么是好的测试
- 能发现错误
- 不冗余(不会重复去测试一些已经测试过的数据或者同类的数据)
- 重点关注软件的最佳特性(1.1.1的②部分已经说过)
- 别太复杂也别太简单
1.4测试的种类
-
单元测试(unit testing): 通常是指验证测试特定代码部分的功能,在功能层面。对每个类、或者是每个类中的方法进行测试。
单元测试的层面为:function / class
单元测试的工具为:JUnit -
集成测试(integration testing): 针对若干个类或若干个包或若干个组件。两个或多个类、包、组件、子系统的组合执行。集成测试的层面为:classes / packages / components / subsystems
-
系统测试(system testing): 测试完全集成的系统,以验证系统是否满足其要求,从而在最终配置中执行软件。 也指在不同的系统上测试,比如程序员一般是开发端,然后开发好后,能在开发端正常运行,但不一定能在用户端正常运行,此时就要在用户端进行一波测试。系统测试的层面:system
-
验收测试: 这个测试在三者之后,关系如图:
-
回归测试: 每次修改bug时,可能会引起其它错误,因此每次修改完代码,最好是把之前的测试用例全都跑一遍。如果每次修改一次代码跑一次,那工作量挺大的,因此有专门的工具在此方面发挥作用。
-
动态测试(dynamic testing): 描述了动态行为的测试代码,它实际上执行具有给定集合的编程代码的测试用例。
-
静态测试: 在没有实际执行程序的情况下执行。Reviews, walkthroughs, or inspections(评审,走查,检查)属于静态测试
为什么有了动态测试还要有静态测试呢?因为有些bug存在与运行无关,举一个例子,假设程序中存在一个除数可能为0的bug,但在运行过程中,只要我们输入的数不为0,就不会发生错误,静态测试就是为了寻找这一类隐藏的bug。
1.5测试vs调试(Testing vs. Debugging)
测试: 发现是否存在错误
调试: 识别错误根源,消除错误。
可以说,是先有测试,发现存在错误了,然后才能去找错误根源并且消除错误。
1.6白盒测试vs黑盒测试(White-box testing vs. Black-box testing)
白盒测试: 对程序内部代码结构的测试
黑盒测试: 关注程序外部功能。对程序外部表现出来的行为的测试
1.7为什么软件测试如此之难
- 不可能暴力穷举完所以可能的测试案例
- 单纯靠偶然测试是没有意义的
- 基于样本的统计数据对软件测试意义不大。比如工厂里测试一批产品合不合格,那就抽样检测,样品中不合格率小于百分之一则该批产品合格,但这对软件测试是没有意义的。
- 对于离散的输入,软件行为差异巨大。 大多数正确,少数点出错。
- 无统计分布规律可循
1.8应该怎么做
要转变心态,用“让其出错”和“尽快出错”作为写高质量代码的日常法宝。抛弃这样的想法:我的代码是宝贝,可不能总让它出错!在测试时候对自己的代码更暴力一些。
2.测试用例
2.1 什么是测试用例
测试用例 = 输入 + 执行条件 + 期望输出
E.g., test cases: {2,4}, {0,0}, {-2,4} for program y=x^2
2.2 什么是好的测试用例
- 最可能发现错误
- 不重复、不冗余
- 最有效
- 既不简单也不复杂
3.测试优先编程
3.1 过程
- 先写spec
- 再写符合spec的测试用例
- 写代码、执行测试、有问题再改、再执行测试用例,直到通过它
3.2 测试驱动开发(TDD)
测试驱动开发(TDD)是一个开发过程,依赖于非常短的开发周期的重复:需求被转化为非常具体的测试用例,然后软件经过改进以通过新的测试。
因此,TDD反对的是允许添加未经证明满足需求的软件的软件开发。即没有被事先证明正确的软件不应该被加入
4.黑盒测试(重点)
4.1黑盒测试的概念
用于检查代码的功能,而不关心内部实现细节
4.2 黑盒测试发现的错误种类
- 不正确或者缺失的功能
- 接口错误
- 数据类型的错误或者外部数据库中访问的错误
- 行为或性能错误
- 初始化和终止时的错误
4.3 黑盒测试的检测样例
黑盒测试的检测样例应该基于规约、要求和外部描述。但是黑盒测试的用例无法覆盖所有的功能要求,用尽可能少的测试用例,尽快运行,并尽可能大的发现程序的错误。这就要看等价类的划分了。这是第一节课的重点!!
4.4 测试用例的等价类划分
(1)概念
基于等价类划分的测试: 将被测函数的输入域划分为等价类,从等价类中导出测试用例。
等价类 : 代表着对输入约束加以满足/违反的有效/无效数据的集合。包括无效等价类和有效等价类
(2)为什么用等价类
基于的假设:相似的输入,将会展示相似的行为。故可从每个等价类中选一个代表作为测试用例即可。从而可以降低测试用例数量
4.5 等价类划分例子:
大数相乘函数:
BigInteger has a method multiply that multiplies two BigInteger values together:
/**
* @param val another BigInteger
* @return a BigInteger whose value is (this * val).
*/
public BigInteger multiply(BigInteger val)
E.g.,
BigInteger a = ...;
BigInteger b = ...;
BigInteger ab = a.multiply(b);
可以明显看出来这是二维输入空间,应该为整数对的的形式 (a,b)除了从正负角度排列出四种可能,还可以考虑,输入的值为特殊值(0,1,-1),输入的数据超出上限怎么办,要求输入的是数字而我输入字符怎么办。
4.6 边界值分析(等价类划分的补充)
即在等价类划分的边界处取值作为测试用例。例如:
- 0是正数和负数的边界
- 数字类型(比如int型)的最大值和最小值
- 集合的空,比如空串、空数组。
- 数组或字符串的第一个和最后一个元素。
例子:(这里主要看如何划分,写出具体例子不做重点)
4.7 测试用例的覆盖
由于输入通常是多维的,每一个维度都具有等价类划分,由此引出两种关于测试用例的选取。两种方法各有优劣。
(1)笛卡尔乘积法(全覆盖)
概念: 多个划分维度上的多个取值,要组合起来,每个组合都要有一个用例
例子: 假如有两个输入,第一个输入对应的等价类划分为{1,2,其它值},第二个输入对应的等价类划分为{5,6,其它值},则测试用例至少选3*3种,有(1,5),(1,6),(1,其它值),(2,5),(2,6),…,(其它值,其它值)。
优缺点 : 这种方法的优点是覆盖完备,缺点是测试用例多,代价大。
注意: 虽然看起来是全排列,但是可能存在组合后互相矛盾的情况。
例子:there’s noway to cover the combination a<b, a=0, b=0,
(2)部分覆盖
**概念 :**每个维度的每个取值至少被1个测试用例覆盖一次即可
例子: 再以上例为例,只要我们选了(1,任意值)的测试用例,则第一个输入的第一个维度就考虑结束了,不必还要继续选(1,5)或(1,6)等等。
优缺点 : 这种方法优点测试用例少,代价小,缺点是覆盖度未必高。
5.白盒测试(非重点)
概念对比
- 黑盒测试:完全从函数spec导出测试用例,不考虑函数内部实现
- 白盒测试:要考虑内部实现细节,根据程序执行路径设计测试用例,白盒测试一般较早执行。
6.测试覆盖(重点!!)
我们学习的覆盖有五种:
- 函数覆盖
- 语句覆盖
- 分支覆盖
- 条件覆盖
- 路径覆盖
测试效果:路径覆盖>分支覆盖/条件覆盖>语句覆盖>函数
测试难度:路径覆盖>分支覆盖>语句覆盖
(1)为什么分支覆盖强于语句覆盖?
如上面简图所示,分支覆盖要检测分支1和分支2,而语句覆盖只需经过左侧的分支1即可,分支覆盖比语句覆盖多一个测试。
(2)注意事项
条件覆盖和分支覆盖不可比较。
但是条件组合覆盖类似于笛卡尔积形式强于分支覆盖
例子:
x or y 的条件覆盖:+ - 和- +
条件组合覆盖: 排列组合四种 + - 、- +、- - 、+ +
6.记录测试策略
概念: lab2的第一步就要写下测试策略,这实质上在写根据什么来选择测试用例,需要在程序中显式记录下来。
目的: 在代码评审过程中,其他人可以理解你的测试,并评判你的测试是否足够充分