6-5软件测试与测试优先的编程
1、Software Testing软件测试
1.1什么是测试
软件测试是提高软件质量的重要手段,是一种来发现程序或者应用程序中的bug的过程,来确认软件是否达到可用级别(用户需求)。它关注系统的某一侧面的质量特性。
即使是最好的测试, 也无法达到100%的无错误
残留缺陷率:1-10%典型的行业软件
0.1-1%高质量的测试,Java库可能达到这种正确性
0.01-0.1%非常好、安全的测试。NASA和Praxis这样
的公司可以达到
1.2测试的特点
测试的目的是发现错误。
再好的测试也无法证明系统里不存在错误。
什么样的测试是好的测试:
1、能发现错误
2、不冗余
3、最佳特性
4、不太复杂也不太简单
1.3测试的层次
单元测试:测试特定的代码段的功能
集成测试:由多个程序员或编程团队创建的多个类、包、组件、子系统的联合执行
系统测试:对一个完全集成的系统进行测试,以验证该系统满足其要求,从而在其最终配置中执行软件
回归测试:
验收测试:
1.4其他的测试类型
Installantion testing、Compatibility testing等
1.5静态测试VS动态测试
静态测试:不执行程序。Reviews, walkthroughs, inspections。
动态测试:描述对代码的动态行为的测试,该测试实际上使用给定的一组测试用例执行已编程代码。
动态测试可以在程序100%完成之前开始,以便测试特定的代码段,并应用于离散的函数或模块。
1.6测试VS调试Testing vs Debugging
测试:发现是否存在错误
调试:识别错误根源,消除错误
1.7白盒测试VS黑盒测试
白盒测试:对程序内部代码结构的测试
黑盒测试:对程序外部表现出来的行为的测试
1.8为什么软件测试很困难
穷举测试是不可行的:大多数情况下输入控件会非常大。
靠偶然测试没有意义
基于样本的统计数据对软件测试意义不大:在物理系统中,可以通过特定的方法加速实验的进程。但是软件的行为通常是离散的,不可预测的。如Pentium FDIV bug
软件行为在离散输入空间中差异巨大,大多数正确,少数出错。bug出现往往不符合特定概率分布。
没有统计分布规律可循。
1.9Pentium FDIV bug and Ariane 5 launch vehicle
奔腾浮点除错误是Intel旧版本Pentium浮点运算器FPU的一个错误,起源于浮点除指令。
Ariane 5 launch vehicle:有未被发现的控制软件错误:⼀个需要接收64位数据的变量为了节省存储空 间⽽使⽤了16位字节,从⽽在控制过程中产⽣了整数溢出,导致导航系统对⽕箭控制失效,程序进⼊异常处理模 块,引爆⾃毁:。
Ariane 5 launch vehicle的启示:
即使是高度关键性的程序也可能有bug测试所有可能输入是解决这样的问题的最好办法
与很多物理工程学上的系统不同,软件的行为是离散的
静态检查有助于发现这个bug
1.10测试时应该:
转变心态:“让其出错”和“尽快出错”,应该抛弃不能总让代码出错的心态。
要学会对自己的代码更暴力。
2、测试用例
2.1什么是测试用例?
测试用例是输入+执行条件+期望结果
测试用例是为特定的目标而开发的,例如执行特定的程序路径或验证特定需求的遵从性。
一个测试用例可能只是你向程序提出的一个问题。运行测试的目的是获取信息,例如程序是否通过测试。
测试用例是质量保证的基石,而它们的开发是为了验证产品的质量和性能。
2.2好的测试用例的特点
1、最可能发现错误
2、不重复,不冗余
3、最有效
4、既不复杂也不简单
3、测试优先编程
先写spec,
再写符合spec的测试用例,
写代码、执行测试、发现问题时修改、在执行测试用例,直到通过。
spec描述了函数输入和输出行为:
1、给出参数的类型及附加约束
2、给出返回值的类型和返回值与输入的关系
3、在代码中规范了方法声明和描述其功能的注释组成
写测试用例,就是理解、修正、完善spec设计的过程
先编写测试是理解规范的好方法:
规范也可能有错误——不正确、不完整、含糊不清、缺少必要的情况,尝试编写测试可以在浪费时间编写有bug的规范实现之前尽早发现这些问题。
4、单元测试
针对软件的最小单元模型开展测试,隔离各个模块,容易定位错误和调试
5、使用JUnit进行自动化单元测试
JUnit是Java中一个被广泛使用的测试库
5.1Junit测试用例
@Test
通常用assertion methods比如assertEquals, assertTrue, 和assertFalse.
例如
注意assertEquals的参数顺序很重要,第一个是期望值,第二个是要进行的测试。==所有JUnit支持的断言都要写成第一个是期望值,第二个是测试值。
如果断言失败会立即返回,JUnit会记录这次失败,其他测试会继续进行。
5.2在现有项目中创建一个新的Junit测试用例
5.3
setUp()实现测试前的初始化工作,需要@Before注释
tearDown()实现测试完成后的垃圾回收等工作,需要@After注释
5.4assertXXX in JUnit
5.5JUnit 测试的组成
为每一个pubic类提供一个测试类
有一个源目录和一个测试目录
5.6举例
在测试时记录下测试策略,如如何分区、特殊值及边界值等
每个测试方法都有一个注释,说明这个测试方法是测试策略中的哪个部分
假设你在为 max(int a, int b) 写测试,它是属于 Math.java 的. 并且你将JUnit测试放在 MathTest.java⽂件中,下面的文字说明应该分别放在哪里?
关于a参数的分区策略:
A.写在Math.java开始的注释里
B.写在MathTest.java开始的注释里
C.写在max()开始的注释里
D.写在JUnit测试的注释里
答案:C
@Test在____之前
A.Math
B.MathTest
C.max()
D.JUnit测试
答案:D
注释"代表a<b"写在___的注释里
A.Math.java开始
B.MathTest.java开始
C.max()开始
D.JUnit测试
答案:D
注释 “@return a和b的最⼤值” 写在
A.Math.java 开头的注释⾥
B. MathTest.java 开头的注释⾥
C. max() 开头的注释⾥
D.JUnit测试的注释里
答案:C
6、黑盒测试
6.1黑盒测试
黑盒测试:用于检查代码的功能,不关心内部实现细节
黑盒测试寻找以下错误:
6.2黑盒测试的测试用例
检查程序是否符合规约
测试用例通常根据规约、设计参数等软件的外部描述来设计
用尽可能少的测试用例,尽快运作,并尽可能大地发现程序的错误
7、通过分区的方法选择测试用例
7.1等价类划分
等价类划分是一种测试方法:将被测函数的输入域划分为等价类 ,从等价类中到存储测试用例。
等价类划分的测试用例的设计需要 针对每个输入数据需要满足的约束条件来划分等价类。
如果一组对象可以通过对称、可传递和自反的关系进行链接,则存在一个等价类
每个等价类代表着对输入约束加以满足/违反的有效或无效数据的集合
相似的输入将会展示相似的行为。故可从每个等价类中选一个代表作为测试用例即可这总方法通过选择不同的测试用例来充分利用有限的测试资源,并使得测试能够探索随机测试可能不能测试到的部分输入空间。从而可以减低测试用例数量。
7.2等价划分类的准则
7.2.1举例BigInteger.multiply()
如图,虽然显示的只有一个参数,但是实际上操作需要两个操作符:传入的参数b及调用这个方法所在的对象a。可以把multiply看做有两个 参数的方法:BigInteger * BIgInteger = BigInteger
所以输入空间是二维的(a, b),将其进行分区:
从正负角度的划分:
-a和b都是正整数
-a和b都是负整数
-a是正整数,b是负整数
-a是负整数,a是正整数
特殊情况:
-a或b是0,1或-1
另外通过考虑方法的运算方法可以根据数据大小划分:
-a或b较小
-a或b的绝对值大于long.MAX_VALUE
最终可以得出划分的点阵:
共有7*7=49个分区,从这里面选择测试用例。
7.2.2举例max()
int * int = int
可以分区成:
-a < b
-a > b
-a = b
7.2.3注意分区之间的边界
上一个例子并没有结束。在测试的时候,我们应该注意一些边界值如
-在正整数和负整数之间的0
-数字类型的最⼤值和最⼩值,例如 int 和 double
-空集,例如空的字符串,空的列表,空的数组
-集合类型中的第⼀个元素或最后⼀个元素
所以7.2.2的例子分区为
a与b的关系
-a < b
-a > b
-a = b
a的值
-a = 0
-a < 0
-a > 0
-a = 最⼩的整数
-a = 最⼤的整数
b的值
-b = 0
-b < 0
-b > 0
-b = 最⼩的整数
-b = 最⼤的整数
7.2.4举例1
以下哪个start的分区是合理的?
start = 0, 0 < start < text.length(), start = text.length()是合理的
以下哪个text的分区是合理的?
text.length() = 0; text.length()-start is odd; text.length()-start is even