“软件可测试性”的相关资料
本篇文章仅作为 "软件可测试性" 的简单概述资料的汇总,方便大家对”可测试性“概念有初步印象和相关概念,不用作任何商业用途。本文系阅读并参考了十余篇关于”可测试性“的文章、文献,从中提取核心内容,并按不同方面进行分类而成,在此分享出来给大家参考、学习使用。后续如果有更新的相关资料,也会汇总上来的。
目录
一、可测试性的概念
1.1 可测试性的基本概念
可测试性(软件领域也称软件测试性,Software Testability)
概念提出:最早是在 20 世纪 70 年代初针对硬件(电子设备)测试提出来的。当时,由于一些硬件电路系统膨胀到一定的量级,对其测试就显得异常复杂。众学者纷纷提出对硬件电路的可测试性度量法方法,于是形成了可测试分析研究分支。
硬件的“可测试性”:可以理解为,测试工程师可以用尽可能简单的方法来检测某种元件的特性,看能否满足预期的功能。
软件的“可测试性”:随计算机发展,到上世纪90年代,逐渐把硬件的可测试分析研究应用到软件上,可测试性概念扩散到了软件领域中。一般认可的概念是:软件发现故障并隔离、定位其故障的能力特性,以及在一定的时间和成本前提下,进行测试设计、测试执行的能力。
当然,软件可测试性并没有一个固定统一的定义,以下有其他的定义:
IEEE标准定义:
(1)为一个系统或构件建立测试标准并通过执行 测试来确定该标准满足的难易程度
(2)对每个声明的需求建立一个测试标准并通过 执行测试来确定该标准被满足的难易程度
Jeffrey Voas 认为:为一个系统或构件建立测试标准并通过执行测试来确定该标准满足的难易程度;对每个声明的需求建立测试标准并通过执行测试来确定该标准被满足的难以程度;
James Bach 认为:软件可测试性就是一个计算机程序能够被测试的容易程度。
从上述定义中可以看出,测试性是一种设计特性。这意味着必须从研制初期就开始考虑产品的测试性,要经过充分的需求分析、设计、开发、试验以后,才能保证产品的测试性要求。
可测试性在测试阶段会对系统的测试成本及后期产品使用过程中的易用性、维护成本等会产生重大影响。如何提高可测性成为软件生命周期特别是前期(设计阶段、构建阶段)重要的一环。
1.2 可测试性的三个常见观点
(1)可测试性是设计出来的
毋容置疑,可测试性不是与生俱来的,而是被设计和实现出来的。可测试性必须被明确地设计,并且正式纳入需求管理的范畴。在研发团队内,测试架构师应该牵头推动可测试性的建设,并和软件架构师、开发工程师和测试工程师达成一致。测试工程师和测试架构师应该是可测试性需求的提出者,并且负责可测试性方案的评估和确认。在研发过程中,可测试性的评估要尽早开始,一般始于需求分析和设计阶段,并贯穿研发全流程,所以可测试性不再是测试工程师的责任,而是整个研发团队的职责。
(2)提升可测试性可以节省研发成本
良好的可测试性意味测试的时间成本和技术成本都会降低,同时还能提升自动化测试的可靠性与稳定性,降低自动化测试的成本。今天在可测试性上的前期投资,会带来后续测试成本的大幅度降低。今天多花的一块钱可以为将来节省十块钱,这点再次证明了“很多时候选择比努力更重要”这一观点。
(3)关注可测试性可以提升软件质量
可测试性好的软件必然拥有高内聚、低耦合、接口定义明确、行为意图清晰的设计。在准备写新代码时,要问自己一些问题:“我将如何测试我的代码?我将如何在尽量不考虑运行环境因素的前提下编写自动化测试用例来验证代码的正确性?”如果你无法回答好这些问题,那么请重新设计你的接口和代码。当你在开发软件时,时常问自己“我将如何验证软件的行为是否符合预期”,并且愿意为了达成这个目标对软件进行良好的设计,作为回报,你将得到一个具有良好结构的系统。
1.3 可测试性的定义标准
- 《GB/T 9414.5-2018 维修性 第5部分:测试性和诊断测试》:确定产品在规定条件下能够被测试的程度的设计特性。
- 《GJB 2547A-2012 装备测试性工作通用要求》:产品能及时准确地确定其状态(可工作、不可工作或性能下降),并隔离其内部故障的一种设计特性。
- 《GJB 451A-2005 可靠性维修性保障性术语》:产品能及时准确地确定其状态(可工作、不可工作或性能下降),并隔离其内部故障的能力。
1.4 硬件可测试性的评估指标
对硬件的评价测试性的指标有很多,最常用的主要包括故障检测率、故障隔离率以及虚警率,但这些指标对软件来说是不太适用的,只能参考借鉴。
- 故障检测率:在规定的时间内,用规定的方法正确检测到的故障数与被测单元发生的故障总数之比。(有故障能否检测出来?)
- 故障隔离率:在规定的时间内,用规定的方法正确隔离到不大于规定的可更换单元数的故障树与同一时间内检测到的故障数之比。(有故障能否准确的定位?)
- 虚警率:在规定的工作时间,发生的虚警数与同一时间内的故障指示总数之比。(没故障能否不要瞎报?)
而针对数字电路系统,目前的可测试性度量方法包括:
SCOAP测度、STAFAN测度 、PREDICT测度、动态的COP测度、动态的SCTM测度等等...
1.5 软件可测试性的评估指标参考表
以下是一个常见的软件可测试性检查表(红色为最常见的关键的评估指标):
- 可操作性:运行地越好,被测试的效率越高;
- 可追踪性:能实时跟踪运行时操作、状态、错误、通信等
- 可观察性:所看见的,就是所测试的;是否可以观察(中间或最后的)测试结果;
- 可控制性:对软件的控制越好,测试越能够被自动执行与优化;是否可以将待测元件的状态控制到如测试条件要求;
- 可理解性:得到的信息越多,进行的测试越灵巧;待测元件是否有说明文档,或是本身可读性很高。
- 可隔离性:待测元件是否可以隔离测试;
- 可分解性:通过控制测试范围,能够更好地分解问题,执行更灵巧的再测试;
- 可自动化性:待测元件是否可以自动测试。
- 简单性:需要测试的内容越少,测试的速度越快;
- 稳定性:改变越少,对测试的破坏越小;
- 异质性:是否需要不同的测试方法及工具平行测试;
对软件的可测试性的评估,其中的四个指标(可观察性、可控制性、可理解性、可追踪性)最为关键,如下图1所示:
图1:测试性模型四个关键因素
关键指标1:可观察性(能否容易的观察程序的行为、输入和输出)
可观测性是指能否容易地观察程序的行为、输入和输出,一般是指系统内的重要状态、信息可通过一定手段由外部获得的难易程度。
- 任何一项操作或输入都应该有预期的、明确的响应或输出,不管是正确的还是错误的甚至是异常的,这样软件才是可测的;
- 当前、过去的系统状态和变量应可见或可查询,“可见”是指运行时可见、维护时可见或者调试时可见,“不可见”就“不可发现”,可测试性就低;
- 所有影响输出的因素可见;
- 可自动检测、报告内部错误;
- 错误输出易于识别,无论通过日志自动分析还是界面高亮显示的方式,要能有助于发现;
- 增加输出参数、减少变量重用,包括打印内部信息、将局部变量作为输出、增加断言、增加局部变量等。
注:最后一条至关重要,因为能够看见结果的前提是输出,而且为了容易测试还要多多输出,此处关联着一个DRR的评估概念。
DRR(Domain/Range Ratio):定义域和值域的比率,也可以理解成输入个数和输出个数的比率。DRR用于度量信息的丢失程度。DRR越大,信息越容易丢失,错误越容易隐藏,测试性也就越低。(输入个数多于输出个数,多个输入只能得到相同的输出,意味着信息丢失)。因此要降低DRR,在输入个数不变的条件下,就要增加输出个数,输出参数越多,能获取的信息越多,也就越容易发现错误。
关键指标2:可控制性(能否容易的控制程序的行为、输入和输出)
可控制性是指能否容易地控制程序的行为、输入和输出,是否可以将被测系统的状态控制到测试条件的要求。一般来讲,可控制性好的系统一定更容易被测试,也更容易实现自动化测试。可控制性一般体现在以下各个方面:
- 提供适当的手段,在外界直接或间接的控制系统的状态及变量,例如对于某些全局类型的变量、特殊结构等,可以进行分类并封装到接口中;
- 采用模块化设计,各模块支持独立测试,对于每个相对独立的模块设计专用的测试驱动和测试桩,模块异常时不影响其他模块的测试(控制测试范围,就能更快地分解、定位问题);
- 业务流程和场景易于分解,可以针对单独业务流程进行测试;
- 提供对外接口,便于构造测试环境模拟外部系统;(如单独针对测试的一些配置/开关等);
- 定时触发、固定大小/空间触发等;
- 提供适当的手段,可以打开或关闭调试输出或打印函数;
测试人员、维护人员能够控制程序的流程、场景、输入和输出,才能有针对性的执行各种用例或实验。通过接口开放流程控制、参数读写功能,也是支持实现自动化测试的基础。
关键指标3:可追踪性(能否容易的跟踪程序的操作、状态、性能、错误、GUI事件以及通信情况)
可追踪性是指能否容易地跟踪系统的行为、事件、操作、状态、性能、错误以及调用链路。可追踪性有助于让你成为“系统侦探”,可以帮助你成为自己系统的福尔摩斯。可追踪性只要体现在以下这些方面:
- 跟踪记录关键函数的持续时间、外部库调用;
- 跟踪记录关键流程的函数执行过程、输入输出参数;
- 严格遵从日志规范,正确设置日志级别,输出日志格式标准统一的,便于自动查询和分析;
- 定义日志类别,区分安全类、业务类、性能类日志,便于问题分析;
- 约定不同类别、不同级别日志的作用及意义;
- 日志文件至少保存 15 天,以便对以“周”为频次发生的异常分析;
追踪的目的是可见。日志是一种维护性可见的方式;运行时以及调试时可见,例如可通过专门的追踪回显代码或者各种追踪工具软件实现。
关键指标4:可理解性(是否提供了足够的信息,易于获取、易于理解、易于使用)
可理解性是指被测系统的信息获取是否容易,信息本身是否完备,并且易于理解。比如被测对象是否有说明文档,并且文档本身可读性以及及时性都有保证。常见的可理解性包含以下这些方面:
- 遵循行业的标准及规范;
- 提供用户文档(使用手册等)、工程师文档(设计文档等)、程序资源(软件源代码等)以及质量信息(测试报告等);
- 文档、接口、流程、代码、注释、提示信息易于理解;
- 被测对象是否有单一且清楚定义的任务,体现出关注点分离;
- 被测对象的行为是否可以进行具有确定性的推导与预测;
- 被测对象的设计模式能够被很好地理解,并且遵循行业通用规范;
二、可测试性需求
2.1 可测试性需求的产生
由于市场及产品用户对产品质量的要求越来越高, 各大企业加强了对产品可测试性需求的收集和控制。目前可测试性需求一般有以下几方面的考虑:
- 面向产品的可测性需求,是为了提高产品的故障检测定位和隔离能力而考虑的可测性需求,直接影响产品问题故障检测定位和隔离的难易程度。面向产品的可测性需求在评审通过后将作为产品本身的规格特性。
- 面向软件验证测试的可测性需求,是为了方便软件验证测试而提出的可测性需求,直接影响测试开发和测试执行的难易程度。
- 面向硬件验证测试的可测性需求,是为了方便硬件验证测试而提出的可测性需求,直接影响测试开发和测试执行的难易程度。
- 面向生产测试的可测性需求,是为了方便生产测试,提高生产测试效率而提出的可测性需求。
2.2 如何提出可测试性需求
产品开发时可测性需求的提出一般按以下操作:
- 产品在提可测性需求时首先参照相应可测性需求基线剪裁得到具体产品的需求基线。相应的要求参见可测性需求基线实施规定。
- 结合具体产品的特点,充分考虑产品各阶段测试的可能遇到的问题和困难,提出相应的可测性需求。
- 参考相关产品的可测性方面的经验案例,提出相应的可测性需求。
- 分析参考业界同类产品的可测性设计特点,提出相应的可测性需求。
2.3 可测试性需求提出的步骤
(1)产品测试需求和策略初步考虑
- 哪些测试是手动测试、哪些是自动测试?
- 测试数据源是内置在系统中,还是外部提供?
- 测试数据的采集和处理是内置,还是外置?
- 测试数据采集装置的控制是内置,还是外置?
- 测试数据源的控制是内置,还是外置?
- 测试数据的处理是内置,还是外置?
(2)关注点主要在于按设置测试控制序列、状态观测点和输入输出机制的需求,主要考虑以下3个方面的内容
- 提供软件模块调试测试的能控性设计,能通过输入设定的测试序列使系统处于某种特定的状态或满足某种特定条件的状态。主要考虑软件模块的调试测试控制点的选择和测试序列导入机制的设计。
- 提供软件模块调试测试的能观性设计,能够通过系统的输出数据判定系统是否处于某种特定的状态或满足某种特定条件的状态。主要考虑软件模块的调试测试观察点的选择和观察装置的设计。
- 软件可测试性需求分为内建、公共、产品特性等三类,内建测试能力与公共可测试性需求合入产品包需求,在确定设计需求时,对具体产品特性需求分析的基础上,提出相应的特性可测试性需求。
三、软件可测试性的战术
3.1 可测试性的目标
从软件架构层面剖析,如下图2所示,软件可测试性战术的目标是:允许在完成软件开发的一个增量后,能够轻松地对软件进行测试评估。
图2:软件可测试性目标
3.2 可测试性的两大战术
软件可测试性,主要有两大战术:一个是提供输入并捕获输出结果进行测试,一个是对软件内部进行监视。
第一个就对应软件测试里的黑盒测试,不看软件内部的构造,只看输入什么,输出什么,而后一个则是白盒测试,要监视软件内部的结果、数据和事件。
图3:软件可测试性战术分析
(1)战术1:输入/输出
- 记录/回放(Record/playback):记录/回放是指将捕获跨接口的信息,并将其作为测试专用软件的输入。
- 将接口与实现分离(Separate interface from implementation):将接口与实现分离允许实现的代替。占位实现允许在缺少被占位组件时,对系统的剩余部分进行测试
- 特化访问路线/接口:具有特化的测试接口允许通过测试工具并独立于其正常操作,来捕获或指定组件变量的值
(2)战术2:内部监视
- 组件可以根据内部状态实现战术,以支持测试过程。
- 内部监视器(Built-in monitors ):组件可以维持状态、性能负载、容量、安全性或其他可通过接口访问的信息。当监视状态被激活时可以记录事件。
3.3 可测试性的属性组成
可测试性战术属性由以下几个部分构成:刺激源、刺激、环境、响应、相应度量。
场景部分 | 可能的值 |
刺激源 | 各类测试人员(单元/集成/系统/验收测试人员、客户) |
刺激 | 一种测试 |
制品 | 设计、代码段、完整的应用 |
环境 | 设计时、开发时、编译时、部署时 |
响应 | 提供测试的状态值、测试环境与案例的准备 |
响应度量 | 测试成本、出现故障的概率、执行时间等 |
以下依次解释:
(1)刺激源
从字面意思上来看,刺激源就是发出刺激的源地址。
对于可测试性来说,刺激软件提高可测试性的来源可能是:单元开发人员、系统集成人员、系统验证人员、测试人。
对于单元开发人员来说,开发完一个单元模块,相应的就需要测试自己所开发的单元模块能否正常运行,因此就产生了需求--可以高效快速的测试;
对于系统集成人员,集成完多个单元模块后,则需要对大模块进行测试,而多个模块之间的依赖、调用则比较复杂,所以需要可以简易、快速的测试;
而系统验证人员则是对开发完成的系统进行测试,该测试阶段属于系统测试,是对整个系统进行测试,需要发现系统中存在的问题,而不需要关心具体的实现细节,只需要专注于BUG的寻找即可,这就需要简单的测试;
而对于测试人员来说,则需要对系统进行完整专业的测试,虽然是专业的测试人员,但是也需要-个可以快速测试的环境;而用户来说,则是需要一个对于他来说相对简单的环境。
(2)刺激
刺激则是刺激源发出的一系列刺激。
对于单元开发人员、系统集成人员来说,每当开发完成一个单元模块,就需要进行测试,这就对应了概念中的已完成的一个阶段,而单元模块开发完成,就相当于一个子系统开发完成。
而对于系统验证人员以及测试人员来说,他们测试的是一个完整的系统,则是一个对应的即将要交付的系统。而用户则需要对开发团队交付的系统进行测试。这些刺激源所发出的刺激,促进了下一阶段的进行。
(3)制品
制品则是相当于每个模块开发完成的单元模块,这个制品是一个小制品,对应的是单元开发人员以及系统集成人员所负责的模块,而下一模块则是大模块的集成,即集成为一个完整的系统,相当于一个大制品。
(4)环境
对于环境来说,则相对的简单了,-般来说对于开发人员等,则是开发环境,则是本机或者公司的服务器当中,这一方面毋庸置疑。而用户则是需要根据其实际的使用场景来选择使用环境,可能是独立的电脑,也有可能是公司的电脑群。
(5)响应
响应则是对于人员的一些动作所进行的一系列响应,如进行测试用例的使用的话,则可以输出测试人员或者用户所期望的结果,如果出现BUG也可以响应的提示出是哪一方面出现的问题,这便是响应。
(6)响应度量
响应度量则是对响应的一个评测,将响应进行数字化,可以进行客观的评价,例如已执行的可执行语句的百分比、最长测试链的长度、执行测试的时间、准备测试环境的时间。而一般来说,时间是最为重要的一个因素。
3.4 可测试性的代码特点
(1)控制性
控制性是指测试者给在被测试的软件提供固定的输入数据的方便程度。换句话说就是软件本身接受定义明确的参数,并且这些参数可由测试者灵活的传入,软件在接受到这些参数后通过一系列运算返回固定的结果。任何软件都应该清楚的表明自己需要什么参数,以及将会生成什么返回值。此外任何软件都应该应该抽象它的依赖,譬如参数或底层模块,并为外部调用者提供随意注入的方式。当然软件代码本身应该清晰,整洁,目标明确.
(2)可见性
可见性是指测试者观察正在测试的软件的当前状态以及它所产生的任何输出的能力。换句话说就是软件应该将内部运算的状态(一般是指错误状态)和输出结果清晰明确的告知测试者。可见性一般都是通过方法执行后验证后置条件完成。
验证后置条件与契约式设计有关。所谓的契约式设计,是指把组件之间的交互描述成契约,权利和义务得到明确的表达和强制实施。
(3)简约性
一般而言,简约性对任何系统在任何情况下都是一个正面的属性,测试毫无疑问也不例外。简单和极其内聚的组件非常适合测试,因为他们暴露出来的方法少,需要的测试也就少,而需要的测试越少,就越能做得可靠,快速。
3.5 代码提高可测试性的常用策略
以下是工作中提高软件系统可测试性的一些经验总结:
- 尽量避免使用静态方法。静态方法不能够利用继承进行覆盖,要替换依赖功能是比较困难的。
- 使用依赖注入(DI)。依赖注入可以很容易的替换真实的业务逻辑,从而把被测对象与依赖环境隔离开来。
- 使用接口。可以利用对接口的实现把模拟功能引入被测试对象中。
- 实例初始化要简单。单元测试过程要对被测试类进行创建和销毁。简化类的实例初始化逻辑,不但有利于编写自动化代码,也可以提高单元测试的运行效率。
另外一些好的设计实践也有利于提高软件系统的可测试性。 例如 S.O.L.I.D 原则。
- S 单一职责原则,一个类只完成一项独立的工作。
- O 开发封闭原则,对象应该对扩展开放,对修改封闭。
- I 接口隔离原则,使用的接口实现对调用类是透明的。
- D 依赖注入原则,高层次模块不依赖低层次模块的实现,而是依赖低层次模块的抽象。
四、软件可测试性的三个层面的工程实践
4.1 代码级别的可测试性
代码级别的可测试性是指针对代码编写单元测试的难易程度。对于一段被测代码,如果为其编写单元测试的难度很大,需要依赖很多技巧或者单元测试框架和Mock框架的高级特性,那往往就意味着代码实现得不够合理,代码的可测试性不好。如果你是资深的开发工程师,并且一直有写单元测试的习惯,你会发现写单元测试本身其实并不难,反倒是写出可测试性好的代码确是一件非常有挑战的事情。
代码违反可测试性的反模式有很多,常见的有以下这些:
- 无法Mock依赖的组件或服务
- 代码中包含未决行为逻辑
- 滥用可变全局变量
- 滥用静态方法
- 使用复杂的继承关系
- 高度耦合的代码
- I/O和计算不解耦
- ...
4.2 服务级别的可测试性
服务级别的可测试性主要是针对微服务来讲的。相对代码级别的可测试性,服务级别的可测试性更容易理解。一般来讲,服务级别的可测试性主要考虑以下这些方面:
- 接口设计的契约化程度
- 接口设计文档的详细程度
- 私有协议的详细设计
- 服务运行的可隔离性
- 服务扇入扇出的大小
- 服务部署的难易程度
- 服务配置信息获取的难易程度
- 服务内部状态的可控制性
- 测试数据构造难易程度
- 服务输出结果验证的难易程度
- 服务后向兼容性验证的难易程度
- 服务契约获取与聚合的难易程度
- 服务资源占用的可观测性
- 内部异常模拟的难易程度
- 外部异常模拟的难易程度
- 服务调用链路追踪的难易程度
- 内置测试(BIST)的实现程度
- ...
4.3 业务需求级别的可测试性
业务级别的可测试性是最容易理解,也是平时大家接触最多的。一般来讲,业务级别的可测试性可以进一步细分为手工测试的可测试性和自动化测试的可测试性。业务需求级别的可测试性有以下典型的场景:
- 登录过程中的图片验证码或者短信验证码
- 硬件U盾 / USB Key
- 触屏应用的自动化测试设计
- 第三方系统的依赖与模拟
- 业务测试流量的隔离
- 系统的不确定性弹框
- 非回显结果的验证
- 可测试性与安全性的平衡
- 业务测试的分段执行
- 业务测试数据的构造
- ...
(本篇文章目前到此为止......后续待更新——有时间再更新吧)
参考资料来源:
[3] 软件测试质量管理:谈软件的「可测试性」 (baidu.com)
[4] 什么是可测试性需求_weixin_43194640的博客-CSDN博客_可测试性需求
[5] 软件质量属性之可测试性 - 程序那点事 - 博客园 (cnblogs.com)
[6] 如何提高软件的可测试性?_百度知道 (baidu.com)
[7] 可测试性战术 - 金猫大帝 - 博客园 (cnblogs.com)