本章主要讲解内容如下
健壮性和正确性
▪ Throwable ▪ Runtime异常、其他异常
▪ Checked异常、Unchecked异常
▪ Checked异常的处理机制: – 抛出、捕获、处理、清理现场、 释放资源等
▪ 自定义异常类
▪ 断言的作用、应用场合
▪ 调试的基本过程和方法
▪ 黑盒测试用例的设计 – 等价类划分、边界值分析
▪ 以注释的形式撰写测试策略
▪ JUnit测试用例写法 ▪ 测试覆盖度
Robustness(鲁棒性/健壮性):
系统 或组件在存在无效输入或压力环境条件时可以正确运行的程度
鲁棒性编程关注异常终止和异常活动的处理
– 对自己做的事情保守(满足specification),对接收的信息自由(能够处理 各类情况)。 严于律己,宽于待人!
Reliability=Robustness+ Correctness
Mean time between failures (MTBF)
平均故障间隔 时间,是指相邻两次故障之间的平均工作时间。
错误处理是指 编程、应用程序和通信中错误的预测、检测和解决。
异常的关系
Error类描 述Java虚拟机内部错误(资源耗尽和系统错误等),多数情况下无需处 理,也无法处理
Exception类描述程序 导致的错误,需要处理
. 异常处 理机制将允许代码将错误或者异常事件传递给调用它的代码。
当方法不能正常完成时,提供 了另外一个退出路径
在进行Java编程时,请关注Exception hierarchy。
▪Exception层次结构也分为两个分支:从RuntimeException派生的异常和不派生的异常
发生RuntimeException是因为发生了编程错误。
-
A bad cast(类型转换)
– An out-of-bounds array access – A null pointer access -
- 发生任何其他异常是因为其他好程序发生了一件坏事,例如I / O错误
-
Trying to read past the end of a file
– Trying to open a file that doesn’t exist
– Trying to find a Class object for a string that does not denote an existing class
如果是RuntimeException,那就是你的错 - 你可以通过针对数组边界测试数组索引来避免ArrayIndexOutOfBoundsException。 - 如果在使用变量之前检查变量是否为null,则不会发生NullPointerException。
未经检查的异常:编程错误,其他不可恢复的失败(Error + RuntimeException) - 编译程序无需任何操作,但未捕获的异常将导致程序失败不要求必须捕获处理
检查异常:每个调用者都应该知道并处理的错误 - 必须被捕获或传播,或者程序将无法编译(编译器检查您是否为所有已检查的异常提供了异常处理程序)要求必须捕获处理,编译器会进行检查)
尝试以编程方式(例如,通过Integer.parseInt())尝试将字符串转换为数字类型,但字符串没有适当的格式。
IllegalArgumentException以编程方式抛出以指示方法已被传递非法或不适当的参数。 您可以为自己的方法重用此异常
方法不仅可以告诉编 译器返回值是什么,也可以声明会出现什么样的错误
会抛出 异常的四种情况
调用的方法抛出了异常
自己的方法抛出了异常
程序存在错误
虚拟机 或者运行时类库错误
如果子类中重写超类的方法,则子类方法声明的受检查 异常不能比超类方法更普遍。
可以抛出更特定的异常(子类型),或者不要在子类 方法中抛出任何异常。
如果超类方法根本不抛出checked异常,那么子类 中方法(重写)也不应抛出。
当try块中出现异常时,将忽略出现异常位置之后的代码,由catch子句进行异 常处理
如果子类方法重写了父类方法,父类方法没有抛出异常,则 子类方法中需要catch所有的checked异常(不能继续传播)
子类继承自父类的方法,在子类 中不能增加异常
Finally部分的代码,是否捕获异常都会被执行。
异常处理机制:异常被抛出时,JVM根据异常中的信息查找处理异常的代 码,通过调用栈进行反向查找,直到找到为止,如果找不到则终止程序
断言用于在运行时检查代码正确性
t. 断 言要优于if-else语句:1.对程序中的假设起到适当的文档作用;2.在实 际运行时不会带来性能问题(在实现运行时断言可被禁止)。
测试 不变性是否保持,同时起到了文档说明作用
快速发现bugs
断言用于检测程序内部的内容
. 在测试和调试阶段打开断言(快速发现和 定位bugs),在程序发布后关闭断言(提升性能)
Java断言语句是与JUnit方法assertTrue(),assertEquals()等不同的机制.▪它们都声明了关于代码的谓词,但设计用于不同的上下文。 ▪应在实现代码中使用assert语句,以便在实现中进行防御性检查。 ▪应在JUnit测试中使用JUnit assertXXX()方法,以检查测试结果。 ▪断言语句不在没有-ea的情况下运行,但JUnit assertXXX()方法始终运行。
用exception来处理期望发生的情况(知道会发生,但是不知何时) 用断言来处理 绝对不该发生的情况
Exception处理错误的输入 数据,断言检查bugs
防御性编程是防御性设计的一种形式,旨 在确保在不可预见情况下软件持续提供功能的能力。
主要思想:如果某个方法被传入了不良数据,即使这 是另一个子程序的错误,方法本身也不会受到破坏。
隔栏是一种容损策略
类的public方法中假设数据是 不安全的,他们负责检查和清洗;private方法则认为被公有方法接收 处理后的数据是安全的。
. 对异常的处理:在开发阶段让异常尽可能明 显的展示出来,而在产品运行时让异常能够自我恢复。 --进攻式编程
调试是发现错误根源并纠正错误的过程。
调试是成功测试的后续步骤。
测试和调试不是增强软件质量的手段,而是 诊断缺陷的方法。
调试是最后的手段
Debugging Process
重现问题
找到原因
解决问题,避免引入回归性错误 ? 总结经验
Reproduce the Bug
首先找到一个可产生失败的小的可重复测试用例。
控制所有的变量是重现的重点 最好的方法是通过构建工具确保构建环境和对象一致
确定出现bug的环境
通过程序模拟硬件平台的细节
通过虚拟机实现不同的操作系统环境
利用逆向设计推断导致错误的输入 –
- Work Backward 根据代码回溯工作的运行顺序
– 2. Explore the Landscape 探测可能的输入值(边界值分析,分支覆盖)
– 3. Force Error Conditions 利用错误条件,判断一些错误是否发生了
– 4. Introduce Randomness 利用随机性
Diagnose
方法1:从假设开始,构造实验, 证明它是对的或者错的。
方法2:从不符合理论 的观察结果开始,修正理论。
插桩
收集和整理数据,评估任意代码并测试相关 条件。
2: Divide and Conquer 分治
3: Leveraging VCS利用版本控制系统
4: Focus on the differences
Post-mortem debugging 事后调试
程序崩溃后调试
涉及追溯技术和内存/内核转储技术
转储信息可自动生成、程序生成或手工生成
From memory dump to printf
”可以显示程序的动态信息,放置 在指定位置,优于 memory dump
日志框架提供 了包含可配置日志记录的功能,可以在运行时和单个功能部件中启用 、禁用或增加详细信息。
There are seven logging levels for logger: – SEVERE – WARNING – INFO – CONFIG – FINE – FINER – FINEST
以上为第七章主要内容主要包括
语法、正则表达式
▪ 健壮性和正确性
▪ Throwable
▪ Runtime异常、其他异常
▪ Checked异常、Unchecked异常
▪ Checked异常的处理机制: – 抛出、捕获、处理、清理现场、 释放资源等
▪ 自定义异常类
▪ 断言的作用、应用场合
▪ 调试的基本过程和方法