<软件工程>SE6 软件测试技术
本文最后更新于:2023年11月3日 凌晨
SE6 软件测试技术
软件测试:按照特定规程发现软件错误的过程。可以分为静态分析和动态测试
- 进行静态分析(人工测试)时,不必运行软件,只是通过对源代码进行分析,检测程序的控制流和数据流,以及发现运行不到的代码、无限循环、未初始化的变量、未使用的数据、重定义的数据等。也可能包括对多种复杂性度量值的计算。静态分析虽然不能取代动态测试,但它是动态测试开始前有用的质量检测手段
- 动态测试(机器测试)技术借助于输入样例(测试用例)来执行软件,一般又分为功能测试(黑盒测试)以及结构测试(白盒测试)
软件测试的目标是尽可能预防、发现和改正错误。一般符合下面 5 个规则才叫软件错误
- 软件未达到产品说明书标注的功能
- 产品出现了产品说明书指明不会出现的错误
- 软件功能超出产品说明书范围
- 软件未达到产品说明书虽未指出却应达到的目标
- 软件测试员认为难以理解、不易使用、运行速度缓慢,或用户认为不好
软件调试:发现所编写软件中的错误,确定错误的位置并加以排除,使之能由计算机或相关软件正确理解并执行的方法和过程
软件测试与软件调试的区别:
- 测试从一个侧面证明程序员的失败,而调试是为了证明程序员的正确
- 测试是以已知条件开始,使用预先定义的程序,且有预知的结果,不可预见的是程序是否通过测试。调试一般以不可知的内部条件开始,除统计性调试外,结果也不可预见
- 测试是有计划的,并要进行测试设计。调试是不受时间约束的
- 测试是发现错误、改正错误、重新测试的过程。调试是一个推理的过程
- 测试的执行是有规律的。调试的执行往往要求程序员进行必要推理以至知觉的飞跃
- 测试经常是由独立测试组在不了解软件设计的条件下完成的。调试必须由了解详细设计的程序员完成
- 大多数测试的执行和设计可由工具支持,而调试时,程序员的工具主要是调试器
软件测试过程模型
-
测试设计
-
环境模型
包括支持其运行的硬件、固件、软件
-
被测对象模型
为了测试,形成被测对象的简化版本。不同的测试技术,对同一被测对象,可产生不同的对象模型
-
错误模型
为了统一认识,定义 “什么是错误”
错误(error):与期望设计间的偏差。该偏差可能产生不期望的系统行为或失效
失效(failure):与所规约系统执行之间的偏差。失效是系统故障或错误的后果
故障(fault):导致错误或失效的不正常的条件。故障可以是偶然性的或系统性的
程序员编写程序,无意或有意犯下错误(error),这些错误会表现为故障(fault)。程序执行故障代码时,会导致失效(failure)
-
-
测试执行
-
测试结果比较
软件测试的原则
- 所有测试都应追溯到用户需求。软件测试的目的在于发现错误,而从用户角度看,最严重的错误是致使程序无法满足需求的错误
- 在测试工作开始前,要进行测试计划的设计。
- 测试应从小规模开始,逐步转向大规模
- 穷举测试是不可能的。
- 为了尽可能发现错误,应由独立的第三方来测试
- 测试只能尽可能发现错误,而不能保证一定发现所有错误
SE6.1 白盒测试
也称为结构测试或逻辑驱动测试。它是直到产品内部工作过程,可通过测试来检测产品内部动作是否按照规格说明书的规定正常进行。
白盒测试按照程序内部结构测试程序,检验程序中每条通路是否都有能按预定要求正常工作,而不顾其功能
白盒测试的主要方法有逻辑覆盖、基本路径测试等。主要用于软件验证
SE6.1.1 路径测试技术
路径测试技术是一种白盒测试技术。其依据程序点逻辑结构,采用程序流程图表达被测程序模型。通过合理地选择一组穿过程序的路径,实现某种测量度量。
# 控制流程图
一种表示程序控制结构的图形工具。基本元素是节点、判定、控制块
graph LR
O( ) --> A((A)) --> B{B} --> C((C)) --> D((D)) --> E( )
B --> G((E)) --> H((F)) --> D
过程块:不能由节点或判定分开的一组程序语句。
判定:一个程序点,此处控制流可以分岔
节点:一个程序点,此处控制流可以结合
# 测试策略
-
路径覆盖(PX):执行所有可能的穿过程序的控制流程途径。如上图有 2 条路径(ABCD、ABEFD)
一般来说,这一测试严格严格地限制为所有可能的入口、出口路径。如果遵循这一规定,则我们说达到了 100% 路径覆盖率。在路径测试中,该策略是最强的,但一般也是不可实现的。
-
语句覆盖(P1):至少执行程序中所有语句一次。上图中共展现了 6 条语句。
如果遵循这一规定,则我们说达到了 100% 语句覆盖率(用 C1 表示)。语句覆盖是最弱的逻辑覆盖准则。
-
分支覆盖(P2):至少执行程序中每一分支一次。上图中有一处分支。
如果遵循这一规定,则我们说达到了 100% 分支覆盖率(用 C2 表示)。分支覆盖是一种比语句覆盖稍强的逻辑覆盖。但若程序中分支的判定是由几个条件联合构成时,它未必能发现每个条件的错误
-
条件覆盖、条件组合覆盖:设计足够的测试用例,使每个判定中所有可能的条件取值(条件取值组合)至少执行一次。
如果遵循这一规定,则我们说实现了条件组合覆盖。只要满足条件组合覆盖,就一定满足分支覆盖。
例子:在语句
if (x > 3 || y < 1) do();
中,只需分支取遍 true、false 就能实现分支覆盖。但条件必须取遍 x>3、x<=3、y<1、y>=1 才能实现条件覆盖。
# 路径选取与用例设计
最小度强制性测试需求是语句覆盖率。
路径选取的一般原则:
- 选择最简单的、具有一定功能含义的入口、出口
- 已选取基础上,选择无循环的路径,选取短的、简单的路径
- 选取没有明显功能含义的路径,研究该路径为什么存在
SE6.2 黑盒测试
黑盒测试也称功能测试或数据驱动测试。它是在已知产品所应具有的功能,通过测试来检测每个功能是否都能正常使用。
在测试时,将程序视为不能打开的黑盒子,在完全不考虑内部结构和内部特性的情况下,测试者在程序接口进行测试。它只检查程序功能是否按照需求规格说明书的规定正常使用,程序是否能适当地接收输入数据而产生正确的输出信息,并保持外部信息的完整性
黑盒测试方法主要有等价类划分、边界值分析、因果图、错误推测等。主要用于软件确认测试
黑盒法是穷举输入测试。只有把所有可能的输入都作为测试情况使用,才能以这种方法查出程序中的所有错误。实际上测试情况有无穷多个,人们不仅要测试所有合法输入,还要对那些不合法但可能的输入进行测试。
黑盒测试发现以下错误
- 功能不对或遗漏
- 界面错误
- 数据结构或外部数据库访问的错误
- 性能上的错误
- 初始化和中止的错误
SE6.2.1 基于事务流的测试技术
是一种功能测试技术,属于黑盒测试。
事务:以 用户 的角度所见的一个工作单元。一个事务由一系列操作组成,用 “事务流” 表达。
# 事务流程图
系统行为的一种表示方法。为功能测试建立了软件动作模式。其中使用了白盒测试中的一些概念,如操作、节点、分支、链 等
graph LR
s( ) --> A(1) --> B(2) --> C(3) --> D(4) --> E(5) --> e( )
subgraph 一个事务
B --> F(6) --> G(7)
end
G --> ee( )
D --> G
- 并生:事务处理产生一个新事务,由此这两个事务继续执行
- 丝分裂:事务处理产生两个新事务
- 汇集:事务的不同活动可汇集一处
- 吸收:一个事务可被另一个事务吸食
- 结合:两个事务结合后产生一个新事务
事务流图是一种数据流图。从操作应用的历史,观察数据对象
事务流图中的分支:抽象了一个复杂的过程。
事务流图存在 “中断”,把一个过程等价地变换为具有繁多出口的链支
# 测试步骤
- 获取事务流程图
- 浏览和复审。主要是对事务进行分类,为设计用例奠定基础
- 用例设计。实现基本事务覆盖。这涉及到覆盖策略、事务选取等
- 测试执行
SE6.2.2 等价类划分技术
根据程序的 I/O 特性,将程序的输入划分为几个等价区段,使得从每个区段内抽取的代表性数据进行的测试等价于区段内任何数据的测试。
等价类:输入域的一个子集。在该子集中,各个输入数据对于揭示程序中的错误都是等效的。对等价类中的某代表值的测试,等价于对该类中其他值的测试
有效等价类:指那些对于软件的规格说明书而言,是合理的、有意义的输入数据所构成的集合。有效等价类被用于实现功能和功能的测试
无效等价类:指那些对于软件的规格说明书而言,是不合理、无意义的输入数据所构成的集合。无效等价类用于测试实现的功能和性能不符合规格的说明书的要求
等价类划分原则:
-
如果输入条件规定了输入数据的取值范围或值的个数,则可以确定一个有效等价类(取值范围内)和两个无效等价类(超过上界、超过下界)
如:值域 [3, 12) 中,有效等价类可以是 4,无效等价类可以是 -12、2147483647
另如:要求传入 2 个数字时,有效等价类可以是 [20,1],无效等价类可以是 [9]、[14,33,59]
-
如果输入条件规定了输入数据的一组值,而且软件要对每个输入值进行处理,则能为每个输入值确定一个有效等价类,为所有不允许的输入值确定一个无效等价类
如:性别(男 / 女)中,有效等价类 2 个是 男、女,无效等价类可以是 武装直升机
-
如果输入条件是一个布尔量,则可以确定一个有效等价类和一个无效等价类
-
如果输入条件规定了输入值必须符合的条件,则可以确定一个有效等价类(符合条件)和一个无效等价类(不符合条件)
确定等价类后,建立等价类表,再根据等价类来设计测试用例。
SE6.2.3 边值分析法
边界值分析是一种最常用的黑盒测试技术。经验表明,大量错误经常发生在输入或输出范围的边界上,因此设计一些测试用例,使程序运行在边界情况附近,这样揭露错误的可能性较大
使用边界值分析测试用例可以遵循以下原则:
- 如果某个输入条件规定了输入、输出值的范围,则应选择刚好等于边界值的数据,以及恰好超过边界值的数据
- 如果某个输入条件规定了输入、输出值的个数,则用最大个数、最小个数、恰多于最大数、恰少于最小数 的数作为测试数据
SE6.2.4 因果图法
设计测试用例的一种工具。通过因果图将功能转换成一张判定表,为判定表的每一列设置用例。
因果图法着重检查各种输入条件的组合。如,两个输入值的乘积超过了存储限制,程序将发生错误。等价划分和边界值分析不能发现此类错误,因为他们未考虑输入情况的组合。
步骤:
- 找出模块的原因和结果
- 分析原因与结果的关系,画出因果图
- 在因果图上标识特定的约束和限制
- 把因果图转化为判定表
- 把判定表的每一列作为依据,设计测试用例
SE6.3 软件测试步骤
软件测试步骤分为四步:
- 单元测试:集中对源代码实现的每个程序单元进行测试
- 集成测试:将已测试过的模块组装起来,主要对与设计效果的软件系统结构的构造进行测试
- 有效性测试(确认测试):已实现的软件是否满足软件需求规约中的各项需求,软件配置是否完全、正确
- 系统测试:把已经过确认的软件纳入实际运行环境中,与系统中其他成分组合到一起进行测试
graph LR
m1[被测模块] --> A1((单元测试)) --已测试的模块--> B
m2[被测模块] --> A2((单元测试)) --已测试的模块--> B
m3[被测模块] --> A3((单元测试)) --已测试的模块--> B
B((集成测试)) --已集成的软件--> C((确认测试)) --已确认的软件--> D((系统测试)) --> F[可交付的软件]
SE6.3.1 单元测试
主要检查软件设计的最小单位:模块。主要采用白盒测试技术,辅以部分黑盒测试技术
该测试以详细设计文档为指导,测试模块内的重要控制路径,对应程序编码
单元测试考虑模块的 4 个特征:
- 模块接口
- 局部数据结构
- 重要的执行路径
- 错误执行路径
单元测试还有开发 驱动模块 和 承接模块
graph TB
subgraph 代替原本控制模块
O(驱动模块)
end
O --> I(被测模块)
I --> L(承接模块)
I --> R(承接模块)
- 驱动模块:相当于被测模块的主程序,它接收测试数据,将测试数据传递给被测模块,最后输出实测结果
- 承接模块(桩模块):由被测模块调用,用以代替被测模块调用的子模块
SE6.3.2 集成测试
测试模块接口的正确性。集成测试与单元测试平行进行。多采用黑盒测试技术并辅以一些白盒测试技术
集成测试是一种软件集成化技术。方式可以是 自顶向下(需设计承接模块)或自底向上(需设计驱动模块)。
集成测试的方法:
- 一次性组装方式:对每个模块分别进行单元测试,再将所有模块组装在一起进行测试
- 增值式组装方式:对每个模块分别进行单元测试,之后逐步组装成较大系统,在组装过程中边连接边测试
# 冒烟测试
一种常见的集成测试方法,是时间关键性的步进机制。让软件项目团队频繁地对项目进行评估。
冒烟测试包括以下活动:
- 将已转换为代码的软件构件集成到构建中。一个构建包含所有数据文件、库、可复用的模块以及实现一个或多个产品功能所需的工程化构件
- 设计一系列测试以暴露影响构建正常完成其功能的错误。其目的是为了发现极有可能造成项目延迟的业务阻塞错误
- 每天将该构建与其他构建及整个软件产品集成起来进行冒烟测试
SE6.3.3 有效性测试(确认测试,验收测试)
有效性测试的目标是发现软件实现的功能与需求规格说明书不一致的错误。采取黑盒测试技术。
graph LR
A(有效性测试) --测试报告--> B(管理机构裁决) --> C(专家鉴定会) --> D[用户运行维护]
A1(软件配置审查) --软件配置--> B
-
确认测试的范围
确认测试对应系统需求,以确保软件符合所有功能、行为、性能的需求测试,确认测试只使用黑盒测试技术。
确认测试必须有用户积极参与,或以用户为主来进行
用户参与设计测试方案,使用用户界面输入数据并分析评价测试的输出结果
-
软件配置检查
确认测试的一个重要内容是复查软件配置,保证软件配置的所有成分都齐全,质量符合要求,文档与程序完全一致,且已经编好目录。在确认测试过程中,还应严格遵循用户指南及其他操作程序,以便检查这些使用手册的完整性和正确性
-
Alpha 和 Beta 测试
若软件专为某个客户开发,可以进行一系列确认测试,以保证用户所有需求得到满足
SE6.3.4 系统测试
集中检验系统所有元素(包括硬件、信息等)之间协作是否合适,整个系统的性能、功能是否达到
系统测试实际上是一系列不同的测试,以下是用于系统测试的几种典型软件系统测试
-
功能测试
在规定时间内运行软件系统所有功能,验证该软件系统有无严重错误
-
恢复测试
以不同方式让软件出现故障,用来检验软件是否恰当地完成恢复
- 如果恢复是自带的,则重新初始化、检测点设置、数据恢复以及重新启动等都是对其正确性的评价
- 如果恢复需要人工干预,则要估算出修复的平均时间,以及确定它是否能在可接受的限制范围内
-
安全性测试
试图去验证建立在系统内的防御机制,以防止来自非正常的侵入
- 充当任何角色,测试者可以通过外部书写的方式得到口令
- 可以用用户设计的软件去破坏已构造好的任何防御的袭击系统
- 可以破坏系统,使系统不能为他人服务
- 可以跳过侵入的恢复过程,而故意使系统出错
- 也可以跳过找出系统的入口钥匙,而放过看到的不安全数据等
-
强度测试
要求一个非正常数量、频率或容量资源的方式下运行一个系统
-
性能测试
测试软件在被组装进系统的环境下运行时的性能
- 性能测试应该覆盖测试过程的每一步,即使在单元层,单个模块的性能也可以通过白盒测试来评价
- 性能测试有时与强度测试联系在一起,常常需要硬件和软件的测试设备
-
可用性测试
从使用的合理性、方便性等角度对软件系统进行检验,以发现人为因素或使用上的问题
-
部署测试(配置测试)
软件必须在多种平台和操作系统环境中运行。部署测试也检查客户将要使用的所有安装程序及专业安装软件,并检查用于向最终用户介绍软件的所有文档
SE6.3.5 测试驱动开发
测试驱动开发是极限编程的重要特点。在开发功能代码前先编写测试代码,然后编写相关功能代码直到满足这些测试用例。然后循环添加其他功能,直到完成全部功能开发
测试驱动的原则:
- 测试隔离。不同代码的测试应该相互隔离
- 测试列表。在任何阶段如果需求功能改变,添加功能时首先把相关功能点加到测试列表中,然后继续手头工作
- 先写断言。编写测试代码时,首先编写对功能代码判断使用的断言,之后编写相应的功能代码
- 可测试性。
- 及时重构。
SE6.3.6 面向对象软件的测试策略
面向对象环境中的单元测试
- 类测试等同于传统软件的单元测试。类包含的操作是最小的可测试单元
- 不独立对单个操作进行测试,而是将其作为类的一部分来测试。
- 面向对象软件的类测试是由封装在该类中的操作和类的状态行为驱动的
面向对象环境中的集成测试
面向对象软件没有明显的层次控制结构,传统的自顶向下或自底向上集成策略意义不大。
面向对象软件的集成测试有两种策略
- 基于线程的测试:对响应系统的一个输入或事件所需的一组类进行集成,每个线程单独地集成和测试,应用回归测试以确保没有产生副作用
- 基于使用的测试:通过测试很少使用服务类的那些类(独立类)开始构造系统。独立类测试完后,利用独立类测试下一层次的类(依赖类),以此类推。
面向对象软件的集成测试中,驱动程序和桩程序的使用也出现了变化
- 驱动程序可用于测试低层的操作和整组类的测试
- 驱动程序也可用于代替用户界面以便在界面实现之前可以进行系统功能的测试
- 需要类间的协作但期中一个或多个协作类仍未完全实现的情况下使用桩程序
簇测试是面向对象软件集成测试中的一步
- 利用试图发现协作中的错误的测试用例来测试协作的类簇
SE6.3.7 web 应用的测试策略
web 应用测试策略采用了所有软件测试的基本原理,并使用面向对象系统所使用的测试策略
web 应用的测试步骤:
- 对 web 应用的内容模型进行评审,以发现错误
- 对接口模型进行评审,保证适合所有的用例
- 评审 web 应用的设计模型,发现导航错误
- 测试用户界面,发现表现机制和导航机制中的错误
- 对功能构件进行单元测试
- 对贯穿体系结构的导航进行测试
- 在各种不同的配置环境下,实现 web 应用,并测试 web 应用对每种配置的兼容性
- 进行安全性测试,试图攻击 web 应用或其所处环境的弱点
- 进行性能测试
- 通过可监控的最终用户群对 web 应用进行测试,对他们与系统的交互结果进行以下方面的评估:内容和导航错误、可用性、兼容性、安全性、可靠性、性能等
web 页面的测试工具
- HttpUnit:在 Junit 之上构建的测试框架,支持 web 应用的黑盒测试和 in-container 容器内测试
- JwebUnit:在 HttpUnit 上创建的辅助工具包。其减少了测试 web程序要编写的代码
- StrutsTestCase:为了测试 Struts 应用而在 JUnit 基础上创建的测试框架
- HtmlUnit:一个具有浏览器基本功能的 Java 组件。Html 是 JUnit 的扩展测试框架之一,它支持 Html 文件,并提供了一些 API,允许访问网页、填写表格、点击链接等
SE6.3.8 软件测试的实施过程
在完成了设计阶段的工作后,准备编码的同时,测试的准备工作也应开始
测试应该尽早地进行,及时地对编写好的单元进行测试,可以避免在编码后期进行测试时所面临的系统庞大、测试工作没有头绪的麻烦。如,在一个会议管理系统设计工作完成后,可以进行分工,一部分人着手编码,另一部分人着手测试
将要采用的测试策略可以通过讨论来确定。
# 测试准备阶段
在这个阶段,确定黑盒测试和白盒测试将采用的方法或工具。
如果采用手工测试的方法,就应根据《系统规格需求说明书》和《系统设计说明书》编写测试用例
如果采用测试工具,就应熟悉其用法
# 单元测试阶段
在编码同步进行的过程中,编码人员每完成一个单元的代码,就应该用上述白盒测试方法对其进行测试,直到所有单元模块都测试通过
# 集成测试阶段
运用黑盒测试方法对整个系统进行测试。
集成测试关注:
- 把各个部分连接在一起,穿越模块接口的数据是否会丢失
- 一个模块的功能是否会对另一个模块的功能产生不利影响
- 各个子功能组合起来是否能达到预期的功能
- 各个子模块的误差累积起来是否会放大,从而达到不可接受的程度
对于类,在集成测试阶段:
- 主要检查其他模块对类的数据有没有意外的篡改,尤其是调用静态方法和属性时
对于 .aspx 页面,集成测试阶段:
- 主要看集成进系统后,这个页面进入的前置条件是否得到其他页面的支持,同时从这个页面能否顺利转到其他页面
- 各页面对 Session、Cookie 等的更改会否产生模块间的干扰,从而出现反常行为
# 系统测试
系统测试注意:
-
测试环境的搭建:
-
各种程序设计语言都有强大的 IDE,集成了强大的测试环境。在系统测试初期可以利用这些环境进行功能的测试
-
系统测试的第二阶段尽量配置真实的应用环境,然后进行系统的性能、用户友好性方面的测试
-
-
测试过程的记录:
- 认真记录问题发生的环境、性质和简要描述
系统测试包括两个方面
- 对功能的测试
- 对性能的测试