少女祈祷中...

软件测试基础

什么是软件测试

软件测试是贯穿整个软件开发生命周期,对软件产品进行验证和确认的活动过程,也是对软件产品质量持续的评估过程,其目的是尽快尽早地发现软件产品中存在的各种缺陷,尽最大可能揭示软件开发过程中所存在的产品质量风险,实现缺陷预防

  • 静态测试:不执行被测对象
  • 动态测试:执行被测对象

充分性问题

  • 由于无法穷举被测软件完整的输入空间,各种软件动态测试方法本质上都是围绕 “如何构造测试集合以使其展现的部分行为能够高效有效地反映软件的整体行为”而展开。判断测试集合在软件上的表现是否能够充分反映该软件的总体表现,在测试领域中叫充分性问题
  • 作用
    • 为分析和度量软件测试质量提供了一条客观的途径
    • 确定测试过程中需要观察的内容
    • 作为测试停止标准之一
    • 当测试集合未能达到期望的充分性准则时,指导测试数据的补充

软件缺陷(Software Defect)

  • 存在于软件(文档、数据、程序)之中的那些不希望或不可接受的偏差
  • Bug是口语化的缺陷

软件失效(Software Failure)

  • 软件运行时产生的一种不希望或不可接受的外部行为结果
  • 系统崩溃,闪退,结果不正确……
    • 外部可见的软件失效是测试中推断软件中存在缺陷的基本方法
    • 没有失效并不代表软件中不存在缺陷

软件故障(Software Fault)

  • 软件运行过程中出现的一种不希望或不可接受的内部状态
  • 内部状态:由所有程序变量的当前值程序指针构成

软件错误(Software Error)

  • 在软件生存期内的不希望或者不可接受的人为错误
  • 软件缺陷本质上是研发人员在软件研发过程中所犯错误在软件中的可视化结果

缺陷具有隐蔽性

  • 软件中一定存在缺陷
  • 有缺陷并不一定产生故障
  • 有故障也并不一定会失效
  • 产生失效一定意味着软件有故障
  • 产生失效一定意味着软件存在缺陷
  • 产生故障一定意味着软件存在缺陷

RIP模型

  • Reachability(可达性):程序中包含必须达到缺陷
  • Infection(感染性):执行位置后,程序的状态必须是不正确的
  • Propagation(传播性):受感染的状态必须传播才能导致某些输出程序的错误

测试用例

A test case is composed of the test case values, expected result, prefix values, postfix values for a complete execution and evaluation of the software under test
—— Paul Amman

  • Test Case Values:为完成被测软件的某些执行所必需的输入值
  • Expected Results:如果程序满足其预期行为,在执行测试时将产生的结果
  • Prefix Values:任何必要的输入,使软件进入适当的状态以接收测试用例的值
  • Postfix Values:任何需要在测试用例值之后发送至软件的输入。通常有两种类型的后缀值:
    • 验证值:查看测试用例值的结果所需的值
    • 退出命令:终止程序或以其他方式使其回到稳定状态所需的值
  • 测试集:与一组测试用例相结合的集合

自动测试用例

  • 4A 模式
    • Arrange: preparation for the coming test
    • Action: execute code under test
    • Assert: compare actual outputs with expected ones
    • Annihilation: clean up test environment

测试用例的度量标准

  • 有效性:发现缺陷的能力
  • 仿效性:测试用例不能发现的缺陷,其它输入发现该缺陷的概率应该非常低
  • 经济性:实现执行成本
  • 可维护性:易于修改的程度

面向代码的测试

单元测试介绍

动态软件测试

开发环境中,通过运行被测代码以验证其是否满足期望目标而进行的一种测试活动以尽早尽可能发现与目标不一致缺陷

单元

单元是一个小粒度的行为特性。从规模上说可能是一个函数、方法、页面,也可能是几个。

单元测试

测试执行是否经济快捷界定单元测试,运行要, 不超过0.1秒
不是单元测试:

  • 跟数据库交互
  • 进行了网络间通信
  • 调用了文件系统
  • 需要对环境做特定的准备(如编辑配置文件)才能运行起来
测试替身(Test Double)

替代真实代码中依赖于数据库、网络和文件系统的代码

单元测试复杂度

被测代码输入需要考虑的

  • 被测代码的输入参数
  • 被测代码内部需要读取的全局变量
  • 被测代码内部需要读取的成员变量
  • 被测代码内部调用的方法/函数获得的数据
  • 被测代码内部调用的方法改写的数据
  • ……
    被测代码输出需要考虑的
  • 被测代码的返回值
  • 被测代码的输出参数(C/C++)
  • 被测代码内部改写的成员变量和全局变量
  • 被测代码进行的文件更新、数据库更新、消息队列更新
  • ……

代码缺陷分类

  • 功能
    • 有特征(静态测试)
      • 语法特征:基于程序语言语法就可以发现的问题
      • 边界行为特征:代码在执行过程中发生异常、崩溃或超时。通常,这些行为由边界条件引起
      • 经验特征:基于过往经验可以发现的问题
    • 无特征(动态测试)
      • 代码行为:代码实现的算法/功能与期望的不一致
      • 部分代码行为:在某些条件下,代码不能准确地完成业务要求实现的功能
  • 性能
    • 时间
    • 空间

单元测试启发式规则

Right—BICEP

  • Right: Are the results right?
  • B: Are all the boundary conditions correct?
  • I: Can you check inverse relationships?
  • C: Can you cross-check results using other means?
  • E: Can you force error conditions to happen?
  • P: Are performance characteristics within bounds?
  • happy path tests: tests should first and foremost validate that the code produces expected results what the users want
B-Boundary conditions
  • 虚假或不一致的输入值
  • 数据格式错误,电话号码错误
  • 可能导致数字溢出的计算
  • 空值或缺失值
I-Checking Inverse Relationship

逆行为测试被测试代码

  • 在数据库中插入一条记录后,查询该记录
  • 已经使用的款项总数 = 款项总数 – 剩余的款项数
C-Cross checking

使用不同数据之间的关系进行测试

  • 已经使用的款项总数 = 款项总数 – 剩余的款项数
E-Forcing Error Condition

使用Mock对象模拟各种异常

P-Performance characteristics

通常用于E2E测试

  • Junit 5 @Timeout

CORRECT

  • Conformance: 数据格式是否与期望的一致
  • Ordering:数据之间的顺序是否满足要求
  • Range:数据是否在合理的最大值和最小值之间
  • Reference:被测代码是否使用了无法控制的外部引用
  • Existence:数据是否被要求存在,例如非空、非0、必须在集合中
  • Cardinality:数据数量是否满足要求
  • Time:每件事情是否按顺发生?是否在正确的时间发生?发生是否及时?
C-Conformance

-单一结构的数据

  • E-mail,phone number……
  • 复合结构的数据
    • (姓名,场地,时间):只没有姓名/场地/时间,只有姓名/场地/时间…….
O-Ordering
  • 一段数据在较大集合中的位置
R-Range
  • Java原始类型范围
  • 自定义范围
  • 不变量断言
R-Reference
  • 被测方法(MUT)在其范围之外引用了什么
  • MUT有什么外部依赖性
  • MUT是否取决于对象处于特定状态
  • 必须存在的任何其他条件
    • 前置条件
    • 后置条件
E-Existence
  • 值为null、零或其他为空
C-Cardinality
  • 只有在这三种情况下(0-1-n规则),某组值的计数才有意义
    • 0
    • 1
    • n(多个)
T-Time
  • 相对时间:如果方法按顺序调用
  • 绝对时间:在边界日测试任何时间敏感代码
  • 并发问题
    • 多个线程同时访问同一对象
    • 需要同步任何全局或实例级数据或方法
    • 外部访问文件或硬件

单元测试实现

测试生命周期

  • Setup (optional): 初始化TestFixture
  • Exercise: 测试与SUT交互,获得结果
  • Verify: 使用一个或多个断言(也称为谓词)将被测系统的结果与预期值进行比较,创建测试结果
  • Teardown (optional): 释放TestFixture,SUT回到初始状态

测试替身

Dummy
  • 在测试方法中不使用其任何方法的测试替身
  • 一般出现在方法的参数处
  • 通常为了防止NullPointerException的出现,保证测试方法可以顺利执行
Fake
  • 一种简化真实代码的测试替身,通常采用继承被Fake对象(真实代码),成为其子类的方法实现
  • 不能作为产品代码,单纯为了测试快,不在测试中出现耗时行为
Stub
  • Provide canned answer:为其调用者提供测试过程中需要使用的信息
  • 通常应用响应待测系统的请求,然后返回特定的值
Spy
  • 使用真实代码的测试替身,返回其真实值
  • 可以打桩
  • 可以记录使用轨迹,便于在后续的测试活动中验证是不是安排的事情按照期望发生
Mock
  • 按照期望实现的用于测试方法中的行为代码
    • 正常路径:返回正常值
    • 异常路径:返回期望的错误/异常

代码测试充分性准则

基于控制流

控制流图(CFG)
  • 结点:语句或代码块
  • 边:控制转移
路径

节点(n1,n2,...nm)(n_1,n_2,...n_m)的序列,其中(ni,ni+1)E,1i<m(n_i,n_{i+1})\in E,1 \leq i < m

  • 长度:路径中边的数量
  • 路径的长度可以为0
  • 路径的子路径是p的子序列
完整路径

从初始节点开始到最终节点结束的路径

语句覆盖(Statement Coverage)
  • 衡量被测代码中的语句得到执行的程度
  • 基于CFG块与语句的对应关系,衡量语句覆盖情况
  • 语句覆盖的正式定义为:测试集合T称为语句覆盖充分的,当且仅当执行T产生的完整路径集合L覆盖了控制流图中的所有节点。如果使用符号Node(G)表示控制流图的节点集合,Node(L)表示L包
    含的节点集合,则测试集合T的语句覆盖率为:
    Node(L)Node(G)100%\frac{||Node(L)||}{||Node(G)||}*100\%
  • 语句覆盖是最弱的标准
分支覆盖(Branch Coverage)
  • 衡量被代码中的所有控制转移被执行的程度
  • 控制转移表现为CFG的边,控制转移得到测试意味着相应的边在测试集合对应的完整路径中出现
  • 分支覆盖准则的定义:测试集合T称为分支覆盖充分的,当且仅当执行T产生的完整路径集合L覆盖了控制流图中的所有边。如果使用符号Edge(G)表示控制流图的边集合,Edge(L)表示L包含的控制流图中的边集合,则测试集合T的分支覆盖率为:
    Edge(L)Edge(G)100%\frac{||Edge(L)||}{||Edge(G)||}*100\%
路径覆盖(Path Coverage)
  • 衡量被代码中的完整路径被执行的程度
  • 被代码中的完整路径对应于CFG中的完整路径
  • 测试集合T称为路径覆盖充分的,当且仅当执行T产生的完整路径集合L覆盖了控制流图中的所有完整路径。如果使用符号Path(G)表示控制流图的所有完整路径集合,则测试集合T的路径覆盖率为:
    LPath(G)100%\frac{||L||}{||Path(G)||}*100\%
基路径覆盖(Prime Path Coverage)
  • 简单路径
    • 除了第一个和最后一个节点,其余节点只出现一次(无内部回路)
  • 基路径
    • 不属于其他简单路径子路径的简单路径
计算基路径
  • 穷举法

  • 节点树

    • 以G中的节点为根节点建立的树,且满足树中除根节点和叶节点可以相同外,从根节点到每个树中节点的路径中,每个节点的出现次数有且仅有 1 次
    • 在节点树中,每条从根节点到叶节点的路径即为一条简单路径
  • 简单节点树

    • 若节点树T不是任何其它节点树的子树,则称节点树T为简单节点树
    • 所有简单节点树的从根节点到叶节点的路径集合为备选的基路径集合
  • 衡量被代码所有基路径被执行的程度

  • 基路径覆盖要求每条基路径至少被执行一次,其定义如下:测试集合T称为基路径覆盖充分的,当且仅当执行T产生的完整路径集合L访问了控制流图中的所有基路径。如果使用符号PP(G)表示控制流图的所有基路径集合,PP(L)表示L访问的基路径集合,则测试集合T的基路径覆盖率为:
    PP(L)PP(G)100%\frac{||PP(L)||}{||PP(G)||}*100\%

判定覆盖(Decision Coverage)
  • 衡量代码中的判定得到执行的程度,期望发现逻辑运算符相关缺陷
  • 如果测试集合能够使得被测代码中的每个判定至少被执行一次,那么则说该测试集合满足了判定覆盖
  • 注意,每个判定被执行一次的含义是指每个判定的所有可能结果都至少出现一次
  • 例 if((num1 >1)&& (num2==0))的真假结果都得到执行,才认为该判定被执行
  • 判定覆盖度:
    判定覆盖度=得到执行的判定数判定总数100%判定覆盖度 =\frac{得到执行的判定数}{判定总数}∗ 100\%
条件覆盖(Condition Coverage)
  • 衡量代码中构成判定的各个条件得到执行的程度,期望发现算术运算符相关缺陷
  • 如果测试集合能够使得被测代码中的每个条件至少被执行一次, 那么则说该测试集合满足了条件覆盖
  • 每个条件被执行一次的含义:每个条件的所有可能结果都至少出现一次
  • 判定覆盖度:
    判定覆盖度=得到执行的条件数条件总数100%判定覆盖度 =\frac{得到执行的条件数}{条件总数}∗ 100\%
  • 满足判定不一定满足条件,满足条件不一定满足判定
判定-条件覆盖(Decision-Condition Coverage)
  • 衡量代码中每个判定以及构成判定的每个条件得到执行的程度
  • 如果测试集合能够使得被测代码中的每个判定至少被执行一次并且构成判定的每个条件至少被执行一次, 那么则说该测试集合满足了判定-条件覆盖
  • 执行的含义同样指所有可能结果都至少出现一次
修正的判定-条件覆盖(Modified Condition/Decision Coverage,MC/DC)
  • 期望构成每个判定的每个条件能独立地影响整个判定的结果
  • 在这里独立地影响整个判定的结果是指在其它条件取值不变的情况下,只改变当前条件的取值就能使得整个判定的结果发生变化

基于数据流

定义和使用
  • 定义:将一个值储存在内存中
    • x出现在一个赋值的左边(x = 44)
    • x是一个程序的输入
    • 在下列情况下应仔细考虑
      • x是调用中的一个实参,方法改变了它的值
      • x是一个方法的形参(当方法开始时隐含的def)
  • 使用:变量被访问
    • x出现在赋值的右边
    • x出现在一个条件测试中
    • x是一个方法的实参
    • x是程序的一个输出
    • x是一个方法在返回语句中的输出
c-use (computation-use)
  • 使用节点 USE(v, n) 是一个计算使用(记做c-use),当且仅当语句n是计算语句(对于计算使用的节点永远有外度=1)
p-use (predicate-use)
  • 使用节点是一个谓词使用(记做p-use),当且仅当语句n是谓词语句(对于谓词使用的节点永远有外度≥2)
Data Flow Graph

给定一个CFG: (N, N0, Nf, E)

  • def(n)def(e) 表示由节点n或边e定义的变量集
  • use(n)use(e) 表示由节点n或边e使用的变量集
  • 数据流图(DFG)可以被定义为一个元组:(ND, N0, Nf, ED), 其中:
    • ND={(n,def(n)use(n))nN}N_D = \{ (n, def(n) \cup use(n)) | n \in N \}
    • ED={(e,def(e)use(e))eE}E_D = \{ (e, def(e) \cup use(e)) | e \in E \}
DU Pairs 和 DU Paths
  • du pair
    • 位置对 (li,lj) ,变量 vli 处被定义,在 lj 处被使用
  • def clear
    • lilj 的路径,在该路径上变量 v 没有在任何节点或边上被重新赋值
  • du path
    • 一个从变量 v 的定义节点到使用节点的简单子路径,满足def clear
    • du(li, lj, v) 变量 v 的du-path集合,从 li 开始,到 lj 结束
    • du(li, v) 变量 v 的du-path集合,从 li 开始
    • du(v) 变量 v 的du-path集合
全定义覆盖(All Defs Coverage)
  • 衡量被测代码中变量的每个定义得到使用的程度
  • 全定义覆盖的正式定义为:测试集合T满足全定义覆盖,当且仅当对于数据流图中每个变量viv_i的每个定义处djd_j,执行T产生的完整路径集合L中存在一条路径lLl \in L的某个子路径π\pi,满足πdu(dj,vi)\pi \in du(d_j, v_i)。若def(vi)def(v_i)是变量viv_i在数据流图中定义处的集合,ΠLD(vi)={ππdu(dj,vi),πlL的子路径}\Pi^D_L (v_i) = \{\pi | \pi \in du(d_j,v_i), \pi 是l \in L的子路径\},且满足ΠLD(vi)\Pi^D_L (v_i)的任意两个元素是viv_i的不同定义处开始的定义使用路径,则测试集合T的全定义覆盖率判定覆盖度:
    Σi=1mΠLD(vi)Σi=1mdef(vi)100%m为程序中变量的个数\frac{\Sigma^m_{i=1} ||\Pi^D_L (v_i)||}{\Sigma^m_{i=1} ||def(v_i)||}∗ 100\%,m为程序中变量的个数
全使用覆盖(All Uses Coverage)
  • 衡量被测代码中变量的每个使用得到执行的程度
  • 全使用覆盖的正式定义为:测试集合T满足全使用覆盖,当且仅当对于数据流图中每个变量viv_i的每个定义djd_j的每个使用uku_k,执行T产生的完整路径集合L中存在一条路径lLl \in L的某个子路径π\pi,满足πdu(dj,uk,vi)\pi \in du(d_j,u_k, v_i)。若use(vi)use(v_i)是变量viv_i在数据流图中定义使用对的集合,ΠLU(vi)={ππdu(dj,uk,vi),πlL的子路径}\Pi^U_L (v_i) = \{\pi | \pi \in du(d_j,u_k,v_i), \pi 是l \in L的子路径\},且满足ΠLU(vi)\Pi^U_L (v_i)的任意两个元素是viv_i的不同定义处开始和使用处结束的定义使用路径,则测试集合T的全使用覆盖率为:
    Σi=1mΠLU(vi)Σi=1muse(vi)100%m为程序中变量的个数\frac{\Sigma^m_{i=1} ||\Pi^U_L (v_i)||}{\Sigma^m_{i=1} ||use(v_i)||}∗ 100\%,m为程序中变量的个数
全定义使用覆盖(All du path Coverage)
  • 衡量被测代码中变量的每条定义使用路径得到执行的程度。
  • 全定义使用路径覆盖的正式定义为:测试集合T满足全定义使用路径覆盖,当且仅当对于数据流图中每个变量viv_i的每条定义使用路径,执行T产生的完整路径集合L中存在一条路径lLl \in L的某个子路
    π\pi,满足πdu(vi)\pi \in du(v_i)。若du(vi)du(v_i)是变量𝑣𝑖在数据流图中定义使用路径集合,ΠL(vi)={ππdu(vi),πlL的子路径}\Pi_L (v_i) = \{\pi | \pi \in du(v_i), \pi 是l \in L的子路径\},则测试
    集合T的全定义使用路径覆盖率为:
    Σi=1mΠL(vi)Σi=1mdu(vi)100%m为程序中变量的个数\frac{\Sigma^m_{i=1} ||\Pi_L (v_i)||}{\Sigma^m_{i=1} ||du(v_i)||}∗ 100\%,m为程序中变量的个数

基于变异

变异测试是通过在被测对象中引入错误并计算出发现的错误数量来评估测试集的缺陷检测能力

  • 应该在代码中引入哪些类型的bug?
  • 如何在代码中引入错误?
  • 当我们说发现一个bug时,我们应该参考什么标准?
  • 如何表达一个测试集的缺陷检测能力?
强杀死变异

给定一个程序P的变异体mMm \in M和一个测试t,当且仅当t在P上的输出与t在m的输出不同时,就说t强杀死变异m

强变异覆盖(SMC)

对于每一个mMm \in M,如果一个测试集能够强杀死m,那么其满足SMC

强杀死变异体意味着在测试集中有一个测试用例满足变异体的可达性、感染性和传播性

弱杀死变异

给定一个程序P的变异体mMm \in M和一个测试t,当且仅当t在P上的执行状态与t在m上的执行状态不同时,我们称t弱杀死变异m

弱变异覆盖(WMC)

对于每一个mMm \in M,如果一个测试集能够弱杀死m,那么其满足WMC

弱杀死变异体意味着在测试集中有一个测试用例满足变异体的可达性和感染性,但不满足传播性

变异得分

MS(T) = |D|/(|L|+|D|)

  • |D|:被杀死的变异体的数量
  • |L|:存活的变异体的数量

面向接口的功能测试

接口测试介绍

  • 将通过单元测试的代码单元逐渐集成在一起直至整个软件系统,以验证单元之间的接口交互是否满足需求
  • 接口:不同的系统或者一个系统中不同的组件之间相互交互的部分

HTTP方法

-GETPOSTPUTHEADDELETEOPTIONSCONNECTPATCHTRACE
请求信息是否有Body部分可以有
成功的响应是否含有Body可以有
安全
幂等
缓存可能
是否支持HTML表单
  • 安全:请求会修改服务器资源
  • 幂等:对一个请求而言,执行一次和多次效果相同
  • 缓存:用以减少请求的处理和响应时间(大响应); 请求包消息头的标头一般使用
    默认值,响应包消息头涉及3个标头:Cache-Control,Etag,Last-Modified

接口测试设计

等价划分

  • 等价划分是一种经典的分而治之的测试设计方法
  • 对分析得到的每个被测因素的输入域以预期结果相同为等价划分原则,划分为不同的等价类集合,划分需满足:
    • 划分覆盖整个待测试域
    • 各个划分部分之间没有交集
    • 以被测因素的约束为标准,划分需包括
      • 有效等价类:符合约束的等价类
      • 无效等价类:不符合约束的所有其它可能存在的情况
等价划分启发式规则
  • 如果某个输入条件规定值的范围,可以确定一个有效等价类和两个无效等价类
  • 如果输入条件规定了一个输入值的集合,可以确定一个有效等价类和一个无效等价类
  • 如果输入条件是一个布尔表达式的条件,可以确定一个有效等价类和一个无效等价类
  • 如果输入条件定义了一个“必须”的情况,比如”标识的第一字符必须是字母”那么可以确定一个有效等价类和一个无效等价类
  • 如果有理由确信,某一等价类中的各元素在程序中的处理有区别,那就把这个等价类分成更小的等价类
等价划分组合
  • 弱一般等价类
    • 只考虑测试因素的有效等价类
    • 要求每个测试因素的每个有效等价类出现一次
  • 强一般等价类
    • 只考虑测试因素的有效等价类
    • 要求测试测试因素的有效等价类的全组合
  • 弱健壮等价类
    • 即考虑有效也考虑无效等价类
    • 对于有效等价类,每个测试因素的每个有效等价类出现一次
    • 对于无效等价类,要求每次组合考虑一个测试因素的一个无效等价类,其余测试因素使用有效等价类
  • 强健壮等价类
    • 即考虑有效也考虑无效等价类
    • 要求测试因素的等价类的全组合

边界测试

  • 边界值:任何值得测试的范围的临界点,通常指等价类的边界,可分为
    • 边界值:明确地定义在规格说明书中
    • 次边界:隐含在软件中必须经过分析才能获得
    • 仅物理量适用,逻辑变量慎重
  • 测试设计思想
    • 取边界点附近的值作为测试用例的输入,可参考如下的设计原则
    • 如果输入条件定义了数值区间(a,b),那么测试用例应包括a、b、稍微比a大、稍微比b大、稍微比a小和稍微比b小等几种情况

从理论上来说,边界值可分为

  • 仅考虑有效区间单个变量边界值(一般边界值):用在最小值,略高于最小值,正常值,略低于最大值和最大值处取变量的值。如果被测变量个数为n,则测试用例个数为4n+1
  • 仅考虑有效区间多个变量边界值同时作用(一般最坏情况边界值):用各个变量的最小值,略高于最小值,正常值,略低于最大值和最大值的笛卡尔积集。如果被测变量个数为n,则测试用例个数为5n
  • 同时考虑有效区间和无效区间单个变量边界值(健壮边界值):除了在最小值,略高于最小值,正常值,略低于最大值和最大值处取变量的值,还要在略超过最大值以及略小于最小值之处值。如果被测变量个数为n,则测试用例个数为6n+1
  • 同时考虑有效区间和无效区间多个变量边界值同时作用(健壮最坏情况边界值):用各个变量的略小于最小值,最小值,略高于最小值,正常值,略低于最大值,最大值和略超过大值的完全组合。如果被测变量个数为n,则测试用例个数为7n

面向系统的功能测试

测试级别最高的一种测试活动,是将已经集成好的软件系统,作为整个基于计算机系统的一个元素,与计算机硬件、外设、某些支持软件、数据和人员等其它系统元素结合在一起, 在实际运行(使用)环境下,对计算机系统进行一系列的组装测试和确认测试

  • 系统测试的对象不仅包括软件,还包括系统软件所依赖的硬件、外部设备和各类接口,其目的在于通过与系统的需求定义作比较,发现软件与系统定义不符合或与之矛盾的地方以及系统各个部分是否可以协调工作
  • 系统测试分为两大类别
    • 功能测试
    • 非功能测试

功能测试用例设计

输入域划分

输入域

一个输入域分类必须满足以下两个性质:

  • 覆盖整个输入域(完备性)
  • 分类之间没有重叠(独特性)
输入域建模
  • 识别待测因素
  • 找到待测特征
  • 划分待测特征
输入域组合策略
  • ACoC全组合值覆盖
    • 必须使用所有特征中的所有特征块组合
    • 数量:Πi=1QBi\Pi^Q_{i=1}|B_i|
  • ECC单值覆盖
    • 每个特征块的一个值必须至少用于一个测试用例中
    • 数量:Maxi=1QBiMax^Q_{i=1}|B_i|
  • PWC全对偶值覆盖
    • 每个特征的每个块的值必须与每个其他特征的每个块的值相结合
    • 数量:(Maxi=1QBi)(Maxj=1,jiQBj)(Max^Q_{i=1}|B_i|)*(Max^Q_{j=1,j\neq i}|B_j|)
  • TWC全T值覆盖
    • 全对偶覆盖的拓展。需覆盖到t个待测特征之间的关系
  • BCC基本值覆盖
    • 基本值:某个待测特征所有输入域划分取值中,使用频率最高的那个取值
    • 一次针对一个被测特征,用其非基本值替代基本值,其余被测特征仍取基本值
    • 数量:1+i=1Q(Bi1)1+\sum^Q_{i=1}(B_i-1)
  • MBCC多基本值覆盖
    • 如果待测特征有多个基本值,那么就要覆盖到所有基本值,其余仍然是对每个待测特征取非基本值

决策表

由四个区域组成:

  • condition stub:条件名称列表
  • action stub:如果规则得到满足,例程将采取或启动的动作
  • condition entry:条件的组合
  • action entry:在条件组合下采取的行动

表中的每一列都是一条规则,它规定了action stub中指定的行动将在哪些条件下发生
一个规则就是一个测试用例

正交实验法

从大量的实验点中挑选出适量的、有代表性的点,依据相应的正交表,合理地安排实验的一种科学的实验设计方法

  • 因子:有可能影响实验指标的条件
  • 因子的水平(或状态):影响实验因子的因素,在正交表中用“0-水平数-1”或“1-水平数”表示
  • 正交表:记为L次数(水平数因子数), 例如L8(41×24)L_8(4^1 \times 2^4)表示实验次
    数为8,1个4水平的因子,4个2水平的因子
测试思想
  • 根据被测软件的规格说明书找出影响其功能实现的操作对象和外部因素,把它们当作因子,而把各个因子的取值当作状态,构造出二元的因素分析表;然后,利用正交表进行各因子的状态组合,构造有效的测试输入数据集
    • 正交表的因子对应被测对象的测试因素
    • 因子的水平可以看成是各测试因素的取值

场景法

功能测试用例实现

Automata Theoretic Approach

W method:用于给定一个FSM作为输入,可以求出能揭错该FSM的所有输入序列

Control Theoretic Approach

Coverage Based Method

面向系统的非功能测试

性能测试

性能测试基础

用户关注的性能
  • 软件对用户操作的响应时间,即从单击一个按钮、一个链接开始,到应用系统把本次操作的结果以用户能察觉的方式展示出来的过程所耗费的时间
  • 用户所感受到的响应时间既包括客观成分,也包括主观成分
系统管理员关注的性能
  • 一类特殊的用户,同样关心响应时间
  • 与系统资源状态相关的信息
  • 与系统的可扩展性、并发能力、最大容量相关的信息
开发人员关注的性能
  • 同样关心响应时间,也同样关系管理员性能
  • 最关注的是使性能表现不佳的因素和由于大量用户访问引发的软件故障
Web前端性能
  • 考察的主要是浏览器的展现和脚本执行时间,与服务器本身并无太大的关系,也与并发用户量的大小并无非常直接的关系
  • Web应用的前端响应时间指浏览器的页面加载时间
    • 浏览器的页面加载时间:包括HTML的解析、对页面上的图片及CSS等文件的获取和加载、客户端脚本Javascript的执行时间以及对页面进行展示所花费的时间
    • 性能黄金法则:只有10%-20%的最终用户响应时间耗费在下载HTML文档上,其余的80-90%耗费在了下载页面中的所有组件上
    • 高性能前端:如何提高浏览器下载和执行资源的并发性,如何让浏览器尽快开始渲染页面,如何让浏览器尽可能充分地利用缓存等问题是前端性能关注的主要问题

性能测试分类

常规的性能测试
  • 正常条件下进行的测试,主要测试正常使用时系统是否满足要求,同时可能为了保留系统的扩展空间而进行一些稍稍超出“正常”范围的测试。
压力测试
  • 在高负载下或极限负载下的测试,更侧重于确定在什么负载条件下系统性能处于失效状态并以此来获得系统能提供的最大服务级别的测试
  • 其目的在于发现在什么条件下系统的性能会变得不可接受
峰值测试
  • 测试系统在高峰交易时段的性能
容量测试
  • 通过不断增加负载直至系统崩溃,确定系统能够正常运行的最大负载
强度测试
  • 迫使系统在异常的资源配置下运行以检查程序对异常情况的抵抗能力,判断系统的稳定性以及系统未来的扩展空间,例
  • 运行CPU使用率在90%左右时以上的场景
  • 正常压力为点击率“1000/s”,那么测试“2000/s”
疲劳测试
  • 连续长时间的性能测试,发现内存泄漏等问题