以RIPR模型为核心驱动的软件测试教学改革
0 引言
软件测试作为提高软件质量的重要方法被广泛应用.大多软件工程教育工作者认为,为了有利于学生后续课程的学习,软件测试需要进入计算机相关专业的早期课程中[1-2].《软件测试技术》课程大多讲授软件测试的基本测试方法、测试过程及测试管理.在早期课程中,由于缺少相关课程基础及工程背景,学生很难掌握软件测试过程及管理的相关技术.因此,《软件测试技术》的教学重点主要集中于软件测试基本方法的教学与实践.
“我就给他家人出了个主意,七个子女谁养老太太,商铺就归谁,老太太养老送终全托给他。其他子女有心意的去尽点心意,没有心意的也不强求。”李敬益估算了下,依据市值这商铺值5万,都归抚养者,有理有据,大家也许能接受,接下来就得逐个攻破。
近些年来,国内高校针对各自在《软件测试技术》课程教学中存在的问题,开展了针对性的教学改革与实践.例如,北京信息科技大学的张红等针对软件测试教育中思维训练欠缺的问题,介绍教学中所实施的改进措施及取得的教学效果[3].齐鲁师范学院的李伯伟等针对软件测试课程与其他专业课程衔接的问题,指出这些课程之间教学内容上存在的联系,并就相关内容如何教学提出改进建议[4].海军工程大学的赵翀等将课堂教学内容以实践为中心重新组织,将实践教学分为两个阶段,逐步培养学生的实践能力,并在实践教学的第二阶段中引入了探索性测试[5].
众所周知,《软件测试技术》课程中主要讲授的两类测试方法:黑盒测试技术与白盒测试技术.然而,每一类测试方法与揭示程序中bug之间的关系,以及两类方法之间的共性关系在教授软件测试过程中往往被忽略,使得学生仅仅掌握了方法而未能把握其本质.受Ammann和Offutt的启发[6],我们在《软件测试技术》中引入RIPR模型,并贯穿于课程教学的始终.教学实践表明,该教学改革能有效提高学生对软件测试本质的认知,起到良好的教学效果.
1 RIPR模型
《软件测试技术》课程中存在三个基本术语需要学生重点把握,我们在教学过程中通过实例反复进行强调.
(3)软件失效(failure).
(2)软件错误(error).
软件故障是指软件源代码中存在的静态缺陷.例如,程序中的一条语句“a++;”被程序员一不小心写成了“a--”,“a--”被称为一个fault/bug.
图覆盖技术先将软件转为一个图,该图可以是流程图,数据流图,或者是软件开发过程中任何一种图.然后,图覆盖技术以不同的覆盖准则来覆盖图中的元素(如节点、边、边对、路径等).考虑bug在不同的情形下会被触发,图覆盖标准要求执行“到达”在测试中的图中的某个位置,即可达性(R),而不是感染(I)或传播(P).如图2所示,bug存在于节点1.然而,在实际测试上下文中我们并不知道bug存在于哪个节点中,因此,我们采用节点覆盖使得程序运行时能到达每一个可能是bug的节点.假设软件测试结束时,一半的节点没有被覆盖,显然,我们很难相信该软件不存在bug.
软件错误是指程序运行过程中在某一时刻所处的不正确的内部状态.该内部状态可以通过程序执行时的代码段、数据段、堆栈段及相关寄存器来表示.
(1)软件故障(fault/bug).
软件失效是指程序执行后被用户观察到与期望不同的外部不正确行为.在JUNIT中通常使用断言来判别预期行为是否与实际运行行为一致.
图1给出了RIPR模型的示意图.为了探测到故障,一个测试必须要到达故障所在的位置.执行到该bug后必须导致不正确的内部状态,也就是说该内部状态必须被感染,从故障到不正确的程序状态存在一个箭头指向.例如,一个for循环“for(int i=0;i<4;i++)”应该被执行4次.假设该循环被程序员错误地写成了“for(int i=1;i<4;i++)”,则该循环少循环一次.在该程序首次循环时,其内部状态存在一个变量“i=1”,而该变量的正确取值应该是“i=0”,此两者存在差异.因此,此时程序的内部状态是“error”.然而,由于某一时刻,程序内部状态空间巨大,程序员不太可能通过监测内部的程序状态来探寻错误行为.该不正确的内部状态必须传播到不正确的最终状态,此最终状态必须能被程序所观察到,即程序员观察到软件产生失效. 程序员采用测试预言来观察最终的程序状态.仅当观察到的程序状态包含了不正确的状态时,软件失效才被揭示出来.
软件测试的本质是希望设计较少测试用例,运行程序,然后观察软件运行过程中是否发生失效的过程.Offutt和Morell独立地提出了fault&failure模型,fault&failure模型给出了软件失效的三个必要条件,简称RIP模型.理解fault&failure模型是把握软件测试基本原理的根本.近年来,Li等进一步地扩展了fault&failure模型,提出了RIPR模型,给出了观察到软件失效的四个基本条件[7].
![width=301,height=346,dpi=110](https://qkk.lw71.com/uploads/image/2018/03/ed728444f7b7d9381bb17787397758bd.png)
图1 RIPR模型示意图
我们在《软件测试技术》教学过程中,首先向学生详细讲解什么是bug,以及bug的类型,然后详细描述RIPR模型.在讲授具体的测试技术时,反复通过RIPR模型分析失败测试用例是如何导致软件失效的.在《软件测试技术》的教学中,我们选择的教学内容如表1所列.下面分别介绍从软件测试方法、回归测试、测试预言三个方面实施以RIPR模型为核心驱动的软件测试教学思路.为了更好地说明,我们借助于一个简单例子程序来进行阐述,该例子程序及其控制流图CFG如图2所示.
2 RIPR模型教学中的应用
因此,从软件故障到软件失效需要满足可达性(Reachability),感染(Infection),传播性(Propagation)和揭示性(Revealability)四个必要条件,简称RIPR模型.
2.1 基于RIPR的测试方法讲授思路
软件测试方法被大致分为黑盒测试技术、白盒测试技术.其两者的主要区别是在白盒测试中软件测试员清楚软件的运行逻辑,而在黑盒测试仅知道软件的规格说明.我们在教学过程中并不严格区分白盒与黑盒测试技术,而是将软件测试技术分为四类:图覆盖技术,谓词测试技术,句法测试技术以及输入划分技术等.
2.3快速房颤患者的房颤患者和肽素与pro-BNP水平呈正相关,Pearson相关系数为0.610(P=0.000)
这处伤口在右腰偏下方向,约有15公分,但伤口又被人用红色丝线很整齐地缝了起来,如同趴了一条巨大的蜈蚣。老马说:“这不是医院缝的,但是缝的人显然很细心。”天气仍然是热,但秦明月徒然感到一阵寒意,他越来越感觉到这事非同小可。老马又说:“这个伤口具体是什么原因还有待检验。”
逻辑覆盖标准要求谓词不仅具有可达性(R),而且要以某种特定的方式执行以影响谓词的结果.也就是说,谓词必须被感染(I).最基本的逻辑覆盖准则是判定覆盖和条件覆盖. 最近的实证研究表明,源代码中88.5%的谓词是单条件谓词.当一个谓词是单个条件时,判定覆盖与条件覆盖是等价的.然而,在安全关键软件(Safety-critical Software)中依然存在大量多条件组合的谓词,对于该类谓词测试依然是一大挑战.我们在教学过程中,强调谓词测试的应用场景.在讲授复杂的逻辑谓词测试准则时,我们基于RIPR模型分析主子句覆盖相关的覆盖准则.
表1 教学内容一览表
![width=651,height=237,dpi=110](https://qkk.lw71.com/uploads/image/2018/03/7d503d0b4b9510a5c5397b53e7d7874d.png)
序号授课内容知识点1测试的基础知识软件测试,调试,fault,error,failure,测试的分级,软件测试流程,程序流程图,程序依赖图,测试工程师的工作,软件测试准则概述,RIPR模型2图覆盖关于图的基本概念,图节点覆盖,边覆盖,边对覆盖全路径覆盖,结构覆盖存在的问题,关于数据流覆盖的基本概念,源代码的图覆盖,设计元素的图覆盖等3逻辑覆盖谓词与子句,谓词覆盖准则,子句覆盖,MCDC覆盖,ActiveClause覆盖,全组合覆盖等,逻辑故障模型,ROR,RAR算法4输入空间划分输入域建模,组合测试,IPO算法,组合策略准则,划分中约束5基于句法的测试基于句法的覆盖准则,基于程序语法,集成与面向对象测试等6回归测试回归测试的概念,回归测试中的主要问题,常见的测试用例选择算法7测试充分度度量测试充分度的概念,程序插桩,不可行测试的处理
句法能够以不同的方式在软件测试中运用.由于基于句法的变异测试方法测试代价较高.在教学过程中,我们主要向学生介绍句法测试的基本思想.基于句法测试的句法覆盖不仅要求到达bug所在的位置,而且需要“变异”版本的程序状态与正确版本存在差异.在执行完成后,程序员必须能观察到这种差异.也就是说,它必须具有传播性(P).
通过作者独特的交接安排,两位演员在颠沛流离中互相交错,爱与恨的勾连情与怨的交织,通过这个宣叙调直达观众内心,催人泪下让人难忘。
尽管我们不能利用RIPR模型直接分析输入域划分,但依然可以使用RIPR模型对输入域划分进行解释.输入域划分存在一个基本的假设:以每一个划分中的元素作为输入时,软件所表现的软件行为是相同的.所以,我们需要到达(R)每一个划分.
实际补浇用材质均为低C、S、P的材质,目的是希望能够减弱冒口下成分偏析,模拟浇注时始终使用的都是同种材质。先按照以往铸造厂补浇的习惯操作,次数设定为一次,补浇钢液温度1590℃,重量为冒口高度的2/5的重量。设定补浇距离首次浇注的时间间隔为2h,观察补浇钢液对型腔内原有钢液的温度场影响,以及对冒口内缩孔高度的影响。根据观察结果,优化调整冒口尺寸及补浇方案。
老婆已躺下,一听丈夫如此说话,一个激灵爬起来:“思雨,你给我说明白,我怎么烦你了?你天天在外面应酬,天天就是陪你那些狗屁客户,陪你那些朋友,我跟守活寡还有什么区别?你看你,你多长时间没碰我了?今晚人家睡不着,等你,你还嫌我烦你!你说,你是不是……”老婆越说情绪越激动,最后抹了眼泪。
![width=519,height=238,dpi=110](https://qkk.lw71.com/uploads/image/2018/03/38ab8fa57142874463e01f1092d7a7cb.png)
图2 实例源程序及其CFG
2.2 基于RIPR的回归测试讲授思路
回归测试是当软件产生变更后,对变更版本进行再次测试的过程.回归测试需要解决的主要问题是如何复用前一版本的软件测试用例集合.回归测试用例集合的选择、排序、以及最小化是回归测试中的主要任务.我们在讲授过程中会介绍常见的回归测试用例选择方法,比如基于程序执行路径、基于程序切片等方法.由RIPR模型可知,回归测试用例选择的本质是选择出与变更代码高度相关并最可能导致软件失效的测试用例.如图2所示,假设节点4的代码被修改,我们希望能够选择到达节点4的测试用例作为回归测试用例.如果学生能理解RIPR模型,其不难理解回归测试用例选择的本质.
2.3 基于RIPR的测试预言的讲授思路
测试预言是软件测试中一个极为重要的问题,其主要用于判定程序执行失效与否.然而,我们在《软件测试技术》的课程教学过程中往往忽视测试预言.在JUNIT中测试预言通常使用断言.例如,在图2的例子中,假设存在输入arr[]={2,7,0},我们使用断言assertEquals("Nonzero in first element",1,NumZero.numZero(arr))来判定该测试用例是否产生软件失效.由RIPR模型可知,我们选择不同的观察域可能导致不同的判断.表2中给出用变量count作为观察值的测试用例及其测试结果.在测试用例1中,程序虽然执行了bug代码,但程序员观察count变量并未产生差异,则认为其成功执行.对测试用例1的观察域进行修改,如表2的1-1所示,增加了对程序中for循环的次数的观察,并修改了断言.通过比较预期结果和实际结果,很容易判定该次测试产生软件失效.
表2 测试用例及其结果
![width=340,height=105,dpi=110](https://qkk.lw71.com/uploads/image/2018/03/464f85f651ebad8c7444e197157cf5ec.png)
ID输入数据预期结果实际结果测试结果1{2,7,0}Count=1Count=1成功1-1{2,7,0}Count=1Count=1失败LoopCount=3LoopCount=2
3 结束语
安徽工程大学计算机与信息学院自2011年首次开设《软件测试技术》课程,我们一直不断对《软件测试技术》的教学内容进行调整.自2016年,开始实施基于RIPR模型驱动的《软件测试技术》教学改革.在教学过程中,我们理顺了各种软件测试技术的方法本质.平时对学生基本概念的考核结果表明,学生对软件测试本质的理解优于教学改革之前.
参考文献:
[1] GAROUSI V, MATHUR A. Current state of the software testing education in north american academia and some recommendations for the new educators[C]//Software Engineering Education and Training (CSEE&T). 2010 23rd IEEE Conference on IEEE, New York:IEEE,2010:89-96.
[2] 王勇,李丽,卢桂馥,等.核心概念与案例驱动的软件测试课程教学研究[J].计算机教育,2016(9):171-174.
[3] 张红,刘京志,胡景凡,等.软件测试教学中思维能力的培养与探索[J].计算机教育,2013(24):1-4.
[4] 李伯伟,马晓娜.软件测试课程教学问题探讨[J].计算机教育,2013(24):89-91.
[5] 赵翀,高鹏.软件测试课程工程实践教学模式的探索与实施[J].计算机工程与科学,2014(S1):51-55.
[6] AMMANN P, OFFUTT J. Introduction to software testing[M]. London: Cambridge University Press,2016.
[7] LI N, OFFUTT J. Test oracle strategies for model-based testing[J]. IEEE Transactions on Software Engineering,2017,43(4):372-395.