Chapter 24: Testing Object-Oriented Applications¶
24.1 面向对象应用测试概述¶
- 为了充分测试 OO 系统,必须完成三件事:
- 扩大测试的定义,使其包含应用于面向对象分析(OO Analysis)和面向对象设计(OO Design)模型的错误发现技术 。
- 显著改变单元测试(Unit Testing)和集成测试(Integration Testing)的策略 。
- 测试用例的设计必须考虑到OO软件的独特特性 。
-
测试 OO 模型
测试和评审 OO 分析和设计模型特别有用,因为在分析、设计和编写代码等阶段会遇到相同的语义结构(如类、属性、操作、消息) 。因此,在分析阶段发现类属性定义的错误,可以避免该问题在后续设计、代码或下一次迭代中产生副作用 。
-
OO 模型的正确性
- 在分析和设计阶段,语义正确性(Semantic Corrctness)可以根据模型对现实世界问题域的一致性进行评估 。
- 如果模型准确反映了现实世界,则它是语义正确的 。
- 应将模型提交给领域专家评审,检查类定义和层次结构的遗漏或歧义 。
- 评估类关系,以确定其是否准确反映了现实对象间的连接 。
- 类与模型的一致性
- 重新审视 CRC 模型和对象关系模型。
- 检查每张 CRC 索引卡的描述,以确定每个被委托的职责是否属于协作者的定义范围。
- 反向检查连接,确保每个被请求服务的协作者都收到了来自合理来源的请求。
- 使用上一步中检查过的反向连接,判断是否需要其他类,或者职责是否在类之间得到了恰当的分配。
- 判断那些被广泛请求的职责是否可以合并为单一职责。
24.2 面向对象测试的策略¶
- 单元测试(Unit Testing):测试单个类
- 最小的可测试单元是封装的类(Encapsulated Class)
- 不能孤立测试单个操作,而必须将其作为类的一部分进行测试 。
- 集成测试(Integration Testing):测试多个类如何协作
- 基于线程的测试(Thread-based Testing):集成响应系统某个输入或事件所需的一组类 。
- 基于使用的测试(Use-based Testing):先测试独立类(几乎不使用其他类的类),再测试依赖类(依赖其他类的类)。
- 簇测试(Cluster Testing):通过设计测试用例,来检验一组协作类(通过检查 CRC 和对象关系模型确定)在协作过程中的错误。
- 确认测试(Validation Testing):测试整个系统是否实现了期望功能
- 不再考虑类与类之间的连接细节
- 可以使用用例进行测试
- 传统的黑盒测试方法可用于驱动确认测试
24.3 面向对象测试的方法¶
-
Berard 方法
Berard 提出了以下方法:
- 每个测试用例应有唯一标识,并明确关联到待测试的类;
- 应说明测试的目的;
- 应为每个测试制定一系列测试步骤,其中应包含:
- 待测试对象的一组指定状态
- 将要作为测试序列而执行的一组消息和操作
- 测试对象时可能出现的一组异常
- 一组外部条件(即软件外部环境的变化,按顺序进行组织)
- 有助于理解或执行测试的补充信息。
- 基于故障的测试(Fault-based Testing)
- 测试人员寻找可能存在的故障。为了确定这些故障是否存在,设计测试用例来检验设计或代码。
- 类测试与类层次结构(Class Testing)
- 继承并不能免除对所有派生类进行彻底测试的必要性。
- 基于场景的测试设计(Scenario-based Test Design)
- 基于场景的测试专注于用户做了什么,而不是产品做了什么。这意味着捕获用户需要执行的任务(通过用例),然后将这些任务及其变体作为测试用例应用。
- 随机测试(Random Testing)
- 确定适用于类的操作
- 定义操作的使用约束
- 确定一个最小测试序列(定义类(对象)最小生命历程的操作序列)
- 生成各种随机(但有效)的测试序列
- 执行其他(更复杂的)类实例生命历程
- 划分测试(Partition Testing)
以类似于传统软件的等价划分的方式,减少测试类所需的测试用例数量。
- 基于状态的划分(State-based):根据操作改变类状态的能力对操作进行分类和测试。
- 基于属性的划分(Attribute-based):根据操作所使用的属性对操作进行分类和测试。
- 基于类别的划分(Category-based):根据每个操作执行的通用功能对操作进行分类和测试。
- 类间测试(Inter-Class Testing)
- 从客户类开始,随机生成操作序列,每发出一条消息就追到对应的服务器类,看它还会发什么消息,一层层追下去,把所有协作的类都串起来测试。
- 行为测试(Behavior Testing)
- 设计的测试应覆盖所有状态。也就是说,操作序列应使得类遍历所有允许的状态。