断言的作用、应用场合
断言:在开发阶段的代码中嵌入,检验某些“假设”是否成立。若成立,表明程序运行正常,否则表明存在错误。
断言即是对代码中程序所做假设的文档化,也不会影响运行时性能。
ps.断言主要用于开发阶段,避免引入和帮助发现bug。实际运行阶段,不再使用断言(运行时断言不是免费的,它们会使代码混乱,因此必须谨慎地使用(不要滥用))。如果一个断言在其本地上下文中很明显,则应忽略。
- 在编程时编写断言是检测和纠正错误的最快和最有效的方法之一。
- 断言用于记录程序的内部工作,增强可维护性
使用断言的主要目的是为了在开发阶段调试程序、尽快避免错误
断言可用于验证:
- 内部不变量。eg. assert x>0;
- 表示不变量RI。eg. checkRep()
- 控制流不变量
- 方法的前置条件
- 方法的后置条件
Note.断言只是检查程序地内部状态是否符合规约。断言一旦false,程序就停止执行。
断言→正确性
错误/异常处理→健壮性
- 按照惯例,public方法上的前置条件是通过抛出特定的、指定的异常显式检查来进行的
- 可以使用断言来测试非公共方法的前置条件
- 在public和nonpublic方法中,都可以用断言来处理后置条件。
调试的基本过程和方法
debug的目的是寻求错误的根源并消除它
Debug是测试的后续步骤:测试发现问题,debug消除问题
Debugging Process
常用方法:假设-检验
- 收集bug相关数据
- 观察数据,做出bug原因的假设
- 决定如何验证
- 通过工具验证假设是否成立
诊断策略:
- Instrumentation/测量:插桩代码,不影响软件的行为,提供对软件行为的深入了解。eg. logging,print
- Divide and Conquer/分治:防狼围栏算法
- Slicing/切片
- Focus on difference/寻找差异:充分利用版本控制系统,找出在哪个commit之后出现了bug症状
- 调试器
调式工具:
- Memory dump/内存转储:硬盘上的一种文件,包含某一特定时间进程内存内容的副本,当进程因某种内部错误或信号而中止时产生。
- stack trace
- Printf:在程序内部各部分展示执行时的动态信息,比使用静态的dump分析更有效。(一旦软件对外发布,所有用于debug的print语句都要去除或禁用)
- Logging:log4j;点击此查看如何使用
- Debugger:breakpoints,etc
黑盒测试用例的设计
黑盒测试:对程序外部表现出来的行为的测试。用于检查代码的功能,不关心内部实现细节
黑盒测试尝试在以下类型中查找错误:
- 功能不正确/缺失
- 接口错误
- 数据结构/外部数据访问错误
- 行为/性能错误
- 初始化/终止错误
测试用例:输入+执行条件+期望结果
好的测试用例:
- 最可能发现错误
- 不重复、不冗余
- 最有效
- 即不简单也不复杂
写测试用例,就是理解、修正、完善你的spec设计的过程
等价类划分
基于等价类划分的测试:将被测函数的输入域划分为等价类,从等价类中导出测试用例。
针对每个输入数据需要满足的约束条件,划分等价类。
每个等价类代表着对输入约束加以满足/违反的有效/无效数据的集合。
基于假设:相似的输入,将会展示相似的行为。故从每个等价类中选一个代表作为测试用例即可。
eg.
边界值分析
大量的错误发生在输入域的“边界”而非中央。
边界值分析方法是对等价类划分方法的补充
以注释的形式撰写测试策略
测试策略(根据什么来选择测试用例)非常重要,需要在程序中显式记录下来
目的:在代码评审过程中,其他人可以理解你的测试,并评判你的测试是否足够充分
JUnit测试用例写法
Unit Testing(单元测试):针对软件的最小单元模型开展测试,隔离各个模块,容易定位错误和调试。
JUnit:单元测试框架
- @Test:指明这是一个测试方法
- assertion methods like assertEquals, assertTrue, and assertFalse.其中参数的顺序是expected first ,actual second
测试覆盖度
代码覆盖度:已有的测试用例有很大程度覆盖了被测程序。
*代码覆盖度越低,测试越不充分,但要做到很高的代码覆盖度,需要更多的测试用例,测试代价高
- 函数覆盖
- 语句覆盖
- 分支覆盖
- 条件覆盖
- 路径覆盖
测试效果:路径覆盖>分支覆盖>语句覆盖
测试难度:路径覆盖>分支覆盖>语句覆盖