Skip to content

测试

基本概念

  • 验证:发现程序中的问题,从而增加对程序正确性的信心

    • 形式化推理(通常称为验证 Verification):验证通过构建形式化证明来证实程序的正确性。手工进行验证是非常繁琐的,而且自动化验证工具的支持仍然是一个活跃的研究领域。尽管如此,程序中的一些小而关键的部分可能会进行形式化验证。
    • 代码审查(Code Review):让其他人仔细阅读并非正式地推理你的代码,是发现错误的一种有效方法。这类似于在你写作文时让其他人进行校对。代码审查有助于发现那些可能被编写者忽视的错误或逻辑问题。
    • 测试(Testing):在精心选择的输入上运行程序并检查结果。测试是验证过程中最常用的方法之一,通过实际运行程序来检测是否存在错误、性能问题或其他类型的问题。
  • 测试优先开发一个功能的流程:

    • 确定目标功能的规格(如参数的类型及其它性质)
    • 根据目标设计测试
    • 完成功能通过测试
    • 设计更强的测试,确保正确完成目标功能

设计测试

  • 测试集的目的:使用尽可能少的资源(测试),覆盖尽可能大的范围
  • 把问题划分为一系列子空间,保证每个子空间有一个测试

    • image.png|275
    • 如 max 函数
    • image.png|250
  • 使用划分的边界

    • 如对于 max(int, int)->int
    • 划分如下
      • relationship between a and b_
      • a < b
      • a = b
      • a > b
      • value of a
      • a = 0
      • a < 0
      • a > 0
      • a = minimum integer
      • a = maximum integer
      • value of b
      • b = 0
      • b < 0
      • b > 0
      • b = minimum integer
      • b = maximum integer
    • 设计测试
      • image.png
  • 全笛卡尔积测试:随所有可能的条件组合测试

  • 每个部分覆盖测试:只设计测试案例使得所有条件都出现过

  • 黑盒测试

    • 只根据对功能的需求(对输入的描述)设计测试案例,而不管功能的具体实现方法
  • 白盒测试

    • 结合功能的实现设计测试案例
  • 测试覆盖率

    • 语句覆盖(弱):是否每个语句至少被某个测试用例执行过。
      • 工业中通常目标达到 100%
    • 分支覆盖(强):分支覆盖考察的是对于程序中的每个 ifwhile条件语句,其真和假两个方向是否都至少被某个测试用例执行过。
      • 100% 是理想的目标,在一些对安全要求高的行业
    • 路径覆盖(更强):路径覆盖考察的是是否每一种可能的分支组合 — 即程序中的每一条路径 ,都至少被某个测试用例执行过。
      • 100% 是不可实现的
  • 单元测试

    • 对于一个良好的程序,每一个模块都应该进行单元测试
    • 对单元进行分割,分别进行独立的测试
    • 出现错误很容易定位错误的位置
    • 一个模块的失败不应该影响到其他的模块
  • 集成测试

    • 对模块的组合,甚至整个程序进行测试
    • 继承测试不能替代单元测试,因为如果只测试单元模块之间的关系,输入是另一个模块的输出,这个输入可能很弱(没有达到规格对前置条件的描述范围),导致模块可能在以后遇到更强的输入时出现错误
  • 自动化测试和回归测试

    • 自动运行一系列测试案例并得到结果(只是自动运行,测试仍然需要自己测试)
    • 修改代码后应该重新运行测试,防止修正 bug 后引入了新的 bug ,这就是回归测试
  • 发现 bug 后因该把引起 bug 的输入引入测试集

  • 无论是白盒还是黑盒测试都要遵循规格说明