软件构造 第六节

1 异常

异常:程序执行中的非正常事件,导致程序无法再按预想的流程执行

Exceptions将错误信 息传递给上层调用者,并报告“案发现场”的信息 通过 throws 退出异常

  • 运行时异常:由程序员在代码里处理不当造成
    可能有类型转换异常,越界异常,空指针异常

  • 其他异常:由外部原因造成

throws 声明“本方法可能会抛出XX异常
throw 抛出XX异常
(try, catch, finally) 捕获并处理XX异常

unchecked 异常 指无效状态异常:方法被调用时,程序处于不能被该方法调用的状态

当要决定是采用checked exception还是unchecked exception的时候,问一个问题:“如果这种异常一旦抛出,client会做 怎样的补救?”

  • 如果客户端可以通过其他的方法恢复异常,那么采用 checked exception
  • 如果客户端对出现的这种异常无能为力,那么采用unchecked exception;

软件构造 第六节
总结:

  • 方法要在定义和spec中明确声明所抛出的全部checked exception
  • 没有声明所有抛出的 checked异常,编译会出错
  • Unchecked异常和Error可以不用处理
  • 如果子类型中override了父类型中 的函数,那么子类型中方法抛出的异常不能比父类型抛出的异常类型 更宽泛
  • 子类型方法可以抛出更具体的异 常,也可以不抛出任何异常
  • 如果父类型的方法未抛出 异常,那么子类型的方法也不能抛出异常。

2 断言与防御式编程

断言:在开发阶段的代码中嵌入,检验某些“假设”是否 成立。若成立,表明程序运行正常,否则表明存在错误。

断言即是对代码中程序员所做假设的文档化,也不会影响运行时性能 (在实际使用时,assertion可以被disabled)

断言一旦false,程序 就停止执行。

外部失败要 使用Exception机制去处理。

断言非常影响运行时的性能

  • 如果参数来 自于外部(不受自己控制),使用异常处理
  • 如果来自于自己 所写的其他代码,可以使用断 言来帮助发现错误

3 代码调试

从最小的测试用例集开始复现错误
软件版本
软件的运行环境
输入数据

在程序内部各部分展示程序执行时的动态信息,比使用静态的dump 分析更有效。

4 日志

通过设定日志级别来确 定要log哪些信息

log结果可被多种渠道加 以处理,可通过设定条件进行过滤,并输出为多种格式

可使用层次化的多个日志记录器

5 测试

用等价划分和边界值分析方法为模块设计测试用例
可用工具 度量一组测试用例对代码的“覆盖度”

  • 等价类划分
  • 边界值分析

确保程序正确性/健壮性的最普遍的手段:测试
1 设计测试用例 2 用JUnit写测试程序 3 自动化测试过程

  1. 先写specification
  2. 再写符合spec的测试用例
  3. 写代码、执行测试、有问题再改、再执行测试用例,直到通过它

6 等价类划分

基于等价类划分的测试:将被测函数的输入域划分为等价类, 从等价类中导出测试用例。

针对每个输 入数据需要满足的约束条件,划分等价类

等价类应该满足:对称、传递、自反
软件构造 第六节

7 边界值分析

由于大量的错误发生在输入域的“边 界”而非*,边界值分析方法是对等价类划分方法的补充

多个划分维度上的多个取值,要组合起来,每个组合都要有一个用例

  • 笛卡尔积:全覆盖
  • 覆盖每个取值:最少1次即可

8 黑盒和白盒测试

黑盒测试完 全从函数spec导出测试用例,不考虑函数内部实现

白盒测试要考虑内部实现细节

  • 根据程序执行路径设计测试用例
  • 白盒测试一般较早执行

9 代码覆盖度

代码覆盖度:已有的测试用例有多大程度覆盖了被测程序

代码覆盖度越低,测试越不充分 但要做到很高的代码覆盖度,需要更多的测试用例,测试代价高

测试效果:路径覆盖>分支覆盖>语句覆盖

测试难度:路径覆盖>分支覆 盖>语句覆盖

适用EclEmma工具来测试代码覆盖度

测试策略(根据什么来选择测试用例)非常重要,需要在程序中显式记录下来
软件构造 第六节