文章目录
- 基本概念
- validation / verification
- input domain / output domain
- deterministic / non-deterministic
- terminate / not-terminate
- Testing
- 概念
- testing 的目的
- Fault, failure, error
- 测试三要素 (3 essential pieces of information)
- 测试输入
- 预期输出
- 执行测试 test execution
- 测试评估 test evaluation
- testability 可测试性
- successful test case
- 软件测试的心理学原则(Principles of software testing psychology)
- 预期测试结果
- 不只靠自己测自己的程序
- 已经被发现很多错误的地方倾向于被发现更多的错误(places with already many discovered errors tend to be discovered even more errors.)
- input domains
- 白盒测试的输入来源(什么内容可以构成 input domains)
- 黑盒 / 白盒测试
- 黑盒的好处
- 黑盒的坏处
- 白盒好处
- 白盒坏处
- Error guessing
- 一些testing的准则
基本概念
validation / verification
- validation: 是否构建了正确的系统
- verification: 是否以正确的方式构建系统
input domain / output domain
- input domain & input domain
- 输入类型中的值集称为输入域,
- 输出类型中的值集称为输出域。
- 上述例子中,输入的类型是
string
和char
,因此根据输入域的定义,输入域应该是所有 string 和 char 的组合;可以表示成 (s,c) 的集合,其中 s 是字符串,c 是字符 - 输出类型是隐式的,因为squeeze的参数是按引用传递的。如果非说 ouput domain,那么 squeeze 函数的 output domain 是所有的
string
- fibonacci 的输入域是所有的
unsigned int
,而输出域也是所有的unsigned int
- 输入域的 value 可能违反 specification,例如如果一个 input 导致了 divided by 0 问题(这是一个规范),那么也是有可能的
- 输入域在不同的系统上可能是不同的
- 有效地选择输入和输出域是非常重要的,但是并不像上面说的这么容易
deterministic / non-deterministic
- deterministic / non-deterministic: 如果对于一个 input,输入 function 之后结果总是相同的,那么这个 function 是 deterministic,否则是 non-deterministic 的。
terminate / not-terminate
- terminate / not terminate:一个程序是否能够正常终止。以下程序是 not-terminate 的
- 无限循环
- 等待事件的程序(例如等待用户输入)
- 上述的
squeeze
和fibonacci
都是termiate
的程序,也就是他们最终都会终止,只不过对于fibonacci
有返回值,因此测试程序可以直接用这个返回值来做判断,而squeeze
没有返回值,因此必须重新查看 input 进去的数组来确定是否正确地执行操作 - 对于那些
not-terminate
的程序,由于我们无法得到最后的结果(没有 return)因此,有些not-terminate
可以通过查看其中间的 observable outputs 来查看,或者有些干脆就对外不可见
Testing
概念
- testing 的目的是找 failure,找不到 failure 的 testing 没意义
- 本课题的主题是上述的最后一个 item:测试不仅仅是检测错误存在与否,而且还涉及对系统及其组件属性进行评估。这意味着软件测试方法被用于评估和确保程序满足其所有要求,包括功能性和非功能性要求。
- testing 必须在程序(或其 components)构建完成时才能进行
testing 的目的
-
这句引语表明测试的目的是为了 证明实现与规范之间存在差异,
-
并且不能用来证明实现是正确的。大多数测试方法的目标是系统地和积极地发现程序中的这些差异。
-
也就是说 testing 是为了发现 difference between specification and implementation
-
testing 和 debug 是不同的。调试是
- 确定程序中怀疑故障的确切性质和位置;
- 修复该故障。通常,调试始于对故障存在某种迹象。调试的目的是 定位故障并修复它们。
因此,我们说 测试的目标是证明程序中存在错误, 调试的目标则是找出这些错误产生原因,并消除或修复它们。
程序验证 (program proving) 旨在显示程序不包含任何错误。 问题在于大多数程序员和质量保证人员没有具备证明程序正确性所需技能。
Fault, failure, error
- fault 是代码中出现错误的行(位置);可能是一行代码,也可能是多行代码
- failure 是有 fault 导致的最终结果和预期的偏差
- error 是 fault 导致的程序在中间过程进入了错误的状态
- 因此 error 和 failure 都是 fault 导致的,他们都是 fault 的结果
- specification 是想要实现
n
∗
n
n*n
n∗n 但是最终的实现是
x
∗
2
x*2
x∗2 因此
return
这一行是一个 fault - 当 input 是 2 以外的值都会导致 failure,因为会造成预期与程序的结果不符,此程序不会导致 error
- 当 input=2 的时候,出现的现象叫 coincidental correctness (偶然正确性)
- 但 coincidental correctness 是一种经常出现的情况;testing 的目的是发现错误,因此选择 input 就变得非常重要,这些 input 最好能够让所有的 failure 都暴露出来,而减少 coincidental correctness 的发生
- 通常 testing 和 debugging 的步骤如下:
- 选择好的 input 来暴露 failure
- 定位导致 failure 的 faults
- 修复或者移除这些 faults
- 重新测试
- 但上述过程中的 2,3 步骤本来就是容易出错的,在修改 faults 的时候就很容易引入新的错误
测试三要素 (3 essential pieces of information)
- **测试输入:**一组测试输入(set of test inputs),这是对于 terminate 的程序,对于 not-terminate 的程序,需要一组测试输入的序列
- **预期输出:**需要设定预期的 output
- 测试环境
测试输入
- 后面的 2-5 章节讲的就是如何选择合适的 test input 可以以覆盖率更高的情况下更加全面地进行测试(这里的覆盖率由多种衡量标准,例如 branch, condition 等;总之要根据某个覆盖率标准来决定 test input 的集合)
预期输出
- 必须提供生成的每个测试输入的预期行为(只有知道预期行为才知道这个 test 是否 failure 还是 success)。这被称为 oracle问题。在许多情况下,可以从正在测试的程序的需求中直接推导出该oracle。例如,评估系统性能的测试用例可能与该系统需求规范中关于性能的特定要求相关联。例如如果面对一个排序算法,那么你的 test 输入是 [1,3,2,5,4] 那么你的预期输出就可以是 [1,2,3,4,5] 因为我的 specification 已经规定了这是个排序算法,因此这样产生的 oracle 往往简单
执行测试 test execution
一旦准备好可执行的测试用例,下一步是在被测程序上执行测试输入,**并记录软件的实际行为。**例如,记录功能测试输入产生的输出,或者测量执行性能测试输入所需的时间。
- 测试执行是测试过程中通常可以自动化的一个步骤。这不仅节省了测试人员的时间,还允许以最小案例进行回归测试。
测试评估 test evaluation
- 将测试输入下程序的实际行为与该测试输入下程序的预期行为进行比较,并确定实际行为是否满足要求。例如,在性能测试中,确定运行一个测试所需时间是否小于所需阈值。
testability 可测试性
- 可测试性 testability 完全取决于 controllability 可控性 和 observability 可观测性
- 可控性就是 tester 能够提供输入的程度
- 可观测性就是 tester 能够观察软件行为的程度
squeeze
程序的可控性和可观测性都很强, 因为 tester 完全可以掌控输入的内容,并且也可以自由地观察输入(通过其他函数可以调用他最后生成的数组来观察 output)- 带 user interface 的程序更难以 控制 和 观测
- 嵌入式软件就更难
successful test case
- 一个测出了 failure 的 test case 才是成功的
软件测试的心理学原则(Principles of software testing psychology)
预期测试结果
- 对预期的 output 和结果的定义是必要的
不只靠自己测自己的程序
已经被发现很多错误的地方倾向于被发现更多的错误(places with already many discovered errors tend to be discovered even more errors.)
input domains
白盒测试的输入来源(什么内容可以构成 input domains)
黑盒 / 白盒测试
- 黑盒测试是基于 specification 的 (仅测试其功能和程序的 features)
- 白盒测试是 specification + 程序的设计和代码
黑盒的好处
- 可以在 program 完成之前就开始测试
- 擅长于测试缺失的功能和程序行为与规范不符。
黑盒的坏处
- 无法检测到已添加到代码中的附加功能或特性。这对于安全关键系统(额外的代码可能会干扰系统的安全性)或需要保密性(额外的代码可能被用来破解安全措施)尤为重要。例如一些安全相关的代码不会出现在 specification 中明确地规定,因此黑盒可能无法测试
白盒好处
- 它测试代码的内部细节,并尝试检查程序可以执行的所有路径,以确定是否发生问题。因此,白盒测试用例可以检查已实施但未指定的任何附加代码。
白盒坏处
- 白盒测试的主要缺点是必须在设计和编码被测试的程序之后才能选择测试用例。此外,如果系统的某些功能未实现,则使用白盒测试可能无法检测到这一点。
Error guessing
- 错误猜测是一种基于直觉和经验的即兴方法。其思想是识别出可能暴露错误的测试用例。
- 基于:
- 错误猜测是即兴行动,因此不是系统化的。 这些笔记中描述的其他技术都是系统化的