单元测试用例设计方法

单元测试的意义

  • 提高代码质量
  • 尽早发现问题: 问题发现的越早,解决问题的成本越低
  • 保证重构正确性: 随着功能的增加,重构几乎不可避免。很多时候我们不敢重构的原因就是担心其他模块因为依赖它而不工作。有了单元测试,只要在改完代码后运行一下单测就知道改动对整个系统影响了,从而可以让我们放心的重构代码
  • 简化调试过程: 单元测试让我们可以轻松的知道是哪一部分出了问题
  • 简化集成过程: 由于各个单元已经被测试,在集成过程中进行后续的测试会更容易
  • 优化代码设计: 编写测试用例会迫使开发人员仔细思考代码的设计和必须完成的工作,有利于开发人员加深对代码功能的理解,从而形成更合理的设计和结构
  • 单元测试就是最好的文档: 单元测试覆盖了接口的所有使用方法,是最好的示例代码。而真正的文档包括注释很有可能和代码不同步,并且看不懂

单元测试用例设计方法

规范 (规格) 导出法

规范 (规格) 导出法将需求”翻译“成测试用例

例如,一个函数的设计需求如下:

函数:一个计算平方根的函数 输入: 实数 输出: 实数 要求: 当输入一个 0 或者比 0 大的实数时,返回其正的平方根; 当输入一个小于 0 的实数时,显示错误信息“平方根非法—输入之小于 0”,并返回 0; 库函数printf()可以用来输出错误信息。

在这个规范中有 3 个陈述,可以用两个测试用例来对应:

  • 测试用例 1:输入 4,输出 2
  • 测试用例 2:输入 — 1,输出 0

等价类划分法

等价类划分法 假定某一特定的等价类中的所有值对于测试目的来说是等价的,所以在每个等价类中找一个之作为测试用例

  • 按照 [输入条件] [有效等价类] [无效等价类] 建立等价类表,列出所有划分出的等价类
  • 为每一个等价类规定一个唯一的编号
  • 设计一个新的测试用例,使其尽可能多地覆盖尚未被覆盖地有效等价类。重复这一步,直到所有的有效等价类都被覆盖为止
  • 设计一个新的测试用例,使其仅覆盖一个尚未被覆盖的无效等价类。重复这一步,直到所有的无效等价类都被覆盖为止

例如,注册邮箱时要求用 6~18 个字符,可使用字母、数字、下划线,需以字母开头

有效等价类 无效等价类
6~18 个字符(1) 少于 6 个字符(2) 多余 18 个字符(3) 空(4)
包含字母、数字、下划线(5) 除字母、数字、下划线的特殊字符(6) 非打印字符(7) 中文字符 (8)
以字母开头(9) 以数字或下划线开头(10)

测试用例:

编号 输入数据 覆盖等价类 预期结果
1 test_111 (1)、(5)、(9) 合法输入
2 t_11 (2)、(5)、(9) 非法输入
3 testtesttest_12345678 (3)、(5)、(9) 非法输入
4 NULL (4) 非法输入
5 test!@1111 (1)、(6)、(9) 非法输入
6 test 1111 (1)、(7)、(9) 非法输入
7 test 测试 1111 (1)、(8)、(9) 非法输入
8 _test111 (1)、(5)、(10) 非法输入

边界值分析法

  边界值分析法使用与等价类测试方法相同的等价类划分,只是边界值分析假定 错误更多地存在于两个划分的边界上

  边界值测试在软件变得复杂的时候也会变得不实用。边界值测试对于非向量类型的值 (如枚举类型的值) 也没有意义

  例如,划分 (ii) 的边界为 0 和最大正实数;划分 (i) 的边界为最小负实数和 0。由此得到以下测试用例:

  • 输入 {最小负实数}
  • 输入 {绝对值很小的负数}
  • 输入 0
  • 输入 {绝对值很小的正数}
  • 输入 {最大正实数}

基本路径测试法

  基本路径测试法是在程序控制流图的基础上,通过分析控制构造的环路复杂性,导出基本可执行路径集合,从而设计测试用例的方法。设计出的测试用例要保证在测试中程序的每个可执行语句至少执行一次

  基本路径测试法的基本步骤:

  • 程序的控制流图:描述程序控制流的一种图示方法
  • 程序圈复杂度:McCabe 复杂性度量。从程序的环路复杂性可导出程序基本路径集合中的独立路径条数,这是确定程序中每个可执行语句至少执行一次所必须的测试用例数目的上界
  • 导出测试用例:根据圈复杂度和程序结构设计用例数据输入和预期结果
  • 准备测试用例:确保基本路径集中的每一条路径的执行