软件测试

一. 软件测试基础

传统:评估一个程序或系统的属性或能力,确定它是否符号其所需结果的活动

软件测试目的:在程序中找错误,检验是否满足需求。

程序交付测试之前,程序员可以找到和纠正超过99%的错误,软件测试的目的就是找到剩下的1%。

image-20211228162545278

软件测试的原则

  • 尽早的、不断的测试
  • 程序员避免检查自己的程序
  • 从小规模开始,逐渐到大规模
  • 设计测试用例时,应该包括合理的输入和不合理的输入,以及各种边界条件
  • 充分注意测试中的聚集现象:80%的错误由20%造成
  • 对测试结果一定要有一个确认过程
  • 严格的测试计划
  • 注意回归测试的关联性(回归测试,小的改动一定要走一遍全部的测试)
  • 妥善保存一切测试文档

测试用例:为特定目的而设计的一组测试输入、执行条件和预期的结果,最小的测试实体

测试用例的设计原则:代表性、可判定性、可再现性

二. 测试过程

image-20211228163600032

image-20211228163635797

三. 测试方法分类

1. 按实施步骤划分

  • 单元测试 Unit Testing
    • 对软件基本组成单元进行测试,也称组件测试,以class为单位;一般是编写该单元的开发人员执行
    • 测试环境:驱动模块(Driver)——被测模块的上一级模块,接收测试数据,把数据传送给所测模块,最后再输出实际测试结果,也就是被谁调用。桩模块(stub)——模拟被测单元所需要调用的其他函数接口,模拟实现子函数的某些功能,重要的是返回一个结果值。
  • 集成测试 Integration Testing——满足软件设计文档
    • 很多单元集成在一起 不能工作?
    • 在单元测试的基础上,将所有模块按总体设计组装成子系统或系统,集成测试的对象是模块间的接口
    • 集成测试的方法(具体案例见后)
      • 整体集成(非增量集成):所有模块一次全部组装,进行整体测试
        • 优点:效率高、工作量低、简单
        • 缺点:错误难以定位和修改(修改时新旧错误混杂)、即使测试通过也会遗漏很多错误
      • 增量式集成测试:逐步将新模块加入测试
        • 自顶向下的增量集成:从主控模块开始,按软件的控制层次结构,以深度优先或广度优先的策略,逐步把各个模块集成在一起(每次只替代一个桩模块,不断进行回归测试)
          • 优点:主要控制和决策机制较早测试;较少需要驱动模块
          • 缺点:桩模块数量巨大;测试高层模块,低层用桩模块替代没有反应真实情况,测试不充分
        • 自底向上的增量集成:从软件结构最底层的模块开始组装测试
          • 优点:不用桩模块
          • 缺点:难以建立信心
        • 三明治集成:一种混合增量式集成策略,综合了自顶向下和自底向上两种方法的优点
  • 确认测试 Validation Testing
    • 检测软件能否按照合同要求进行工作,满足软件需求说明书的确认标准
    • 确认测试一般是开发人员来做
  • 系统测试 System Testing
    • 系统测试是将已经集成好的软件系统作为一个元素,与计算机硬件、外设、某些支持软件、数据和人员等其他元素结合在一起,在实际运行环境下进行的一系列测试
    • 功能测试(不管代码好坏)、压力测试(资源超负荷)、安全性测试、恢复测试(失败中恢复)、GUI测试、安装测试(目标环境中进行安装,用户亲自测试)
  • 验收测试 Verification Testing
    • 以用户为主的测试
    • 测试
      • 是用户在开发环境下进行的测试
      • 是用户在实际使用环境下进行的测试
  • 回归测试 Regression Testing
    • 验证对系统的变更有没有影响以前的功能,并且保证当前功能的变更是正确的
    • 发生在软件测试的任何阶段,包括单元测试、集成测试和系统测试,频繁重复性劳动
    • 范围:有选择性地执行以前地测试用例

2. 按使用的测试技术划分

  • 静态测试:走查/评审

  • 动态测试:白盒/黑盒

3. 按软件组装策略划分

image-20211228165259847

  • 非增量测试:整体集成
    • image-20211228165336183
  • 增量测试:自顶向下、自底向上、三明治(双向)
    • 自顶向下
      • image-20211228165920464
    • 自底向上
      • image-20211228170004185
    • 三明治
      • image-20211228170022985

四. 黑盒测试

1. 黑盒测试概述

不考虑程序的内部逻辑结构和特性,只依据程序的需求规格说明书

image-20211228173120360

测试用例所覆盖的规格说明范围越大,就越优良

设计良好的测试用例,使之尽可能完全覆盖软件的规格说明

2. 等价类划分方法

等价类:输入数据的某个子集,子集内数据等价,假定”测试某等价类的代表值就等于对这一类的其他值的测试“

在每一个等价类中选取少量有代表性的数据作为测试的输入条件,就可以用少量代表性的测试数据,并取得较好的测试结果

等价类划分基本原则:每个可能输入只属于一个等价类;用等价类某成员作为输入存在问题,用其他成员作为输入也能检测到同样的问题

  • 有效等价类:对于程序的规格说明来说是 合理的、有意义的输入数据构成的集合(如,年龄0-100)
  • 无效等价类:对于程序的规格说明来说是 不合理的或无意义的输入数据构成的集合(如,年龄-1、200)
    • 无效等价类至少一个,也可能多个
    • 无效等价类的作用:可以用于程序对无理输入的应对

image-20211228174333826

确定等价类的六大原则:

  • 输入条件规定了取值范围或值的个数的情况下,则可以确立1个有效等价类和2个无效等价类
    • image-20211228174445098
  • 输入条件规定了输入值的集合或者规定了“必须如何”的条件的情况下,可确立1个有效等价类和1个无效等价类
  • 输入条件是一个布尔量的情况下,可确定1个有效等价类和1个无效等价类
  • 规定了输入数据的一组值(假定n个)、并且程序要对每一个输入值分别处理的情况下,可确立n个有效等价类和1个无效等价类
  • 规定了输入数据必须遵守的规则的情况下,可确立1个有效等价类(符合规则)和n个无效等价类(从不同角度违反规则)
  • 在确知已划分的等价类中各元素在程序处理中的方式不同的情况下,则应再将该等价类进一步的划分为更小的等价类

测试用例={测试数据+期望结果}

测试结果={测试数据+期望结果+实际结果}

image-20211228175027502
image-20211228175158430

3. 边界值方法

边界值分析是等价类测试的特例,主要是考虑等价类的边界条件,在等价类的“边缘”选择元素

大量的错误是发生在输入或输出范围的边界上,而不是发生在输入输出范围的内部

选取正好等于、刚刚大于、刚刚小于边界的值作为测试数据,而非选取等价类中的典型值作为测试数据

image-20211228180213875

边界值分析的原则

  • 如果输入条件规定了值的范围,则应取刚达到这个范围的边界的值,以及刚刚超越这个范围边界的值作为测试输入数据(10公斤至50公斤,应取10、50、10.01、49.99、9.99及50.01等)边界及边界±r
  • 输入条件规定了值的个数,则用最大个数、最小个数、比最小个数少1,比最大个数多1的数据作为测试数据
  • 将原则1和原则2应用于输出条件,即设计测试用例使输出值达到边界值及其左右的值
  • 程序的规格说明给出的输入域或输出域是有序集合,则应选取集合的第一个元素和最后一个元素作为测试用例
  • 如果程序中使用了一个内部数据结构,则应当选择这个内部数据结构的边界上的值作为测试用例(如,a[0..n],应该选取a[0]与a[n]作测试用例,栈选栈空、栈满作测试用例)
  • 分析规格说明,找出其它可能的边界条件
image-20211228181004230
image-20211228180947276

五. 白盒测试

1. 白盒测试概述

又称结构测试或逻辑驱动测试

白盒测试更加充分,主要是程序员在使用

白盒测试的目的:发现代码中的错误

白盒测试中的输入数据从程序结构导出,期望输出从需求规格中导出

image-20211228183013943

image-20211228183029678

2. 白盒测试的覆盖标准

最彻底的白盒测试是覆盖程序中的每一条路径,但是由于程序中一般含有循环,所以路径的数目极大,要执行每一条路径是不可能的,只能希望覆盖代码的程度尽可能高些

image-20211228183257672

image-20211228183334520

image-20211228183358054

image-20211228183410769

若每个分支都有语句,那么语句覆盖=判定覆盖

条件覆盖不保证覆盖全部分支,即条件覆盖不包含判定覆盖,反之也是

image-20211228184015675

3. 基本路径法

路径测试:设计足够多的测试用例,覆盖被测试对象中的所有可能路径

测试中覆盖所有路径的组合是不现实的,把覆盖的路径数压缩到一定限度内,例如,程序中的循环体只执行一次

基本路径测试:程序控制图的基础上,通过分析控制构造的环行复杂性,导出基本可执行路径集合,从而设计测试用例,使得每一个 基本独立路径 至少执行一次。步骤如下:

  • 程序的控制流图
  • 程序圈复杂度:确定程序中每个可执行语句至少执行一次所必须的测试用例数目的上界
    • 流图中区域的数量
    • 给定流图G的圈复杂度V(G),定义为V(G)=E-N+2,E是流图中的边数量,N是流图中节点数量
    • 给定流图G的圈复杂度V(G),定义为V(G)=P+1,P是流图G中判定结点的数量
  • 导出测试用例
  • 准备测试用例:确保基本路径集中的每一条路径的执行
    • 测试用例 = {测试数据+期望结果}
    • 测试数据是由路径和程序推论出来的;预期结果是从函数说明中导出,不能根据程序结构中导出

控制流图

image-20211228185448686

image-20211228185524323

  • 判断中的条件表达式是由一个或多个逻辑运算符 (OR, AND, NAND, NOR) 连接的复合条件表达式,则需要改为一系列只有单条件的嵌套的判断

image-20211228185728362

image-20211228185810590

独立路径(基本路径):一条程序执行的路径,至少包含一条在定义该路径之前的其他基本路径中所不曾用过的边(即:至少引入程序的一个新处理语句集合或一个新条件)