单元自动化测试中类的抽象内存模型研究

 计算机测量与控制.2022.30(2) 犆狅犿狆
狌狋犲狉犕犲犪狊狌狉犲犿犲狀狋牔犆狅狀狋狉狅犾
 ·84 ·
收稿日期:20211123; 修回日期:20211221。基金项目:国家自然科学基金项目(U1736110
)。作者简介:杜婉莹(1997),女,湖北鄂州人,硕士,主要从事软件自动化测试和程序静态分析相关领域方向的研究。通讯作者:王雅文(1983)
,女,陕西凤翔人,博士,副教授,博士生导师,主要从事软件自动化测试和程序静态分析相关领域方向的研究。
引用格式:杜婉莹,王雅文.单元自动化测试中类的抽象内存模型研究[J].计算机测量与控制,2022,30(2):8494.
文章编号:16714598(2022)02008411  DOI:10.16526/j
.cnki.11-4762/tp.2022.02.013  中图分类号:TP3文献标识码:A
单元自动化测试中类的抽象内存模型研究
杜婉莹,王雅文
(北京邮电大学网络与交换技术国家重点实验室,北京 100876
)摘要:由于面向对象程序具有多态性等复杂特性,在软件单元测试中仅凭静态分析难以判断指针和引用指向对象的具体类型,为了解决这一问题,对类的抽象内存模型进行研究,并提出类的操作语义模拟算法;在路径分析时,通过构建和更新抽象内存模型,从而对变量所属类的范围进行限定;对于单元测试,对基于输入域的随机测试进行优化,提出基于路径的随机测试方法,得到输入变量的类型集合;实验表明,类的抽象内存模型结合操作语义模拟算法能够有效提取出路径中类相关的约束,基于路径的随机测试方法比起基于输入域的随机测试方法能够明显提高测试效率。
关键词:面向对象;单元测试;抽象内存模型;符号表;静态分析;测试用例
犚犲狊犲犪狉犮犺狅狀犃犫狊狋狉犪犮狋犕犲犿狅狉狔犕狅犱犲犾狅犳犆犾犪狊狊犲狊犻狀犃狌狋狅犿犪狋犲犱犝狀犻狋犜犲狊狋犻狀犵
DUWanying
,WANGYawen(StateKeyLaboratoryofNetworkingandSwitchingTechnology,BeijingUniversityo
fPostsandTelecommunications,Beijing
 100876,China)犃犫狊狋狉犪犮狋:Duetothepolymorphismandothercomplexcharacteristicsofobject-orientedprog
rams,itisdifficulttojudgethespecifictypesofobjectspointedbypointersandreferencesonlybystaticanalysisinsoftwareunittesting
.Inordertosolvethisproblem,theabstractmemorymodelofclassesisstudied,andanop
erationsemanticsimulationalgorithmofclassesisproposed.Duringpathanalysis,theclassscopeto
whichthevariablebelongsislimitedbyc沈阳音乐学院附属艺术学校
on structingandupdatingtheabstractmemorymodel.Forunittesting,therandomtestingbasedontheinp
utdomainisoptimized,andapathbasedrandomtestingmethodisproposedtoobtainthetypesetofinputvariables.Exp
erimentsshowthattheabstractmemorymodelofclassescombinedwiththeoperationsemanticsimulationalgorithmcaneffec tivelyextracttheconstraintsrelatedtoclassesinthepath,andthepathbasedonrandomtestmethodcansignificantlyimprovetheefficiencyoftestingcomparedwiththeinp
utdomainbasedonrandomtestmethod.犓犲狔
狑狅狉犱狊:object-oriented;unittesting;abstractmemorymodel;symboltable;staticanalysis;testcase0 引言
如今,信息产业的蓬勃发展离不开信息技术的发展,涉及到了通信设备、计算机、软件等一系列领域。随着市场对软件质量要求和软件自身复杂度的不断提高,软件测试的重要性和软件测试在软件工程中
所占的比例越来越大[1
]。据统计,随着对软件可靠性
要求的提高,软件测试会占用40%乃至60%的总开
发时间[2
],其中,单元测试作为软件测试的重要环
节,其目的是检测各个单元模块的故障缺陷,会占用60%左右的测试工作所花费的时间。与手工测试相比,自动化测试能降低系统测试和维护等阶段的成本、有效提高测试的效率和质量,同时能够显著提高
测试覆盖率、大大扩展测试深度[
3]
,自动化测试工具的研发正逐渐受到重视。
目前已有的自动化测试工具有Logiscop
e、PRQA、 
投稿网址:www.jsjcly
kz.com
第2期
杜婉莹,等:
单元自动化测试中类的抽象内存模型研究
·85 ·
Macabe、DevPartner、Purify等[4]
,这些测试工具要么分析代码的语法漏洞、要么统计程序执行时的数据,又或者是对功能的可行性和效率进行检测等。在这之中,单元测试工具有Cpp
Unit、C++Test、VectorCAST、VisualUnit等,它们能对程序进行静态分析、也能生成测试框架代码,然而大部分都不支持测试用例自动生成功能,需要依赖人工来完成测试用例生成操作,因此,对测试用例生成方法的研究具有重要的理论和实践价值
[5]
面向对象程序具有封装、继承、多态三大特性。其中,继承使子类可以直接拥有父类定义的属性和操作,减少代码冗余,增强代码的复用性和可扩展性;多态能使同一操作在不同子类中有不同的具体实现,
让对象以适合自己的方式响应事件[6]
。当子类重写父
类函数,并让父类的指针或引用指向子类对象时触发。多态的存在令程序的编写仅需指明要执行的操作,在实际执行时编译器会根据对象所属的具体类型来调用相应的方法,从而表现出不同的行为,灵活性更高
[7]
。对于包含存在于继承体系中的类对象的源程
序,仅凭静态分析,编译器无法判断程序中类型转换语句的结果以及被调用函数所属的类
[8
]。
在测试用例生成时,如果需要对类对象进行实例化,对象类型的选取是用例生成效率和有效性的一个影响因素。静态生成测试用例开销更小,也具有一定的挑战性,而面向路径的测试用例生成在白盒测试中非常常见。其中,符号执行使用符号来表示程序的输入数据,模拟执行被分析程序,用符号表达式操作代替程序中对变量和参数的操作,是路径分析的一种常用手段。在这一方面,国内外都有不少研究人员参与了研发工作,例如,Euclide定义了内存动态管理的
操作语义模型[9]
,许中兴等人提出了虚拟数组建模内存[1011],赵云山等人设计实现了针对数值型变量的符号执行系统[12]。在单元测试中,当被测函数的输
入变量中含有父类引用时,如何选择类进行实例化,当被调用函数是某个基类的成员函数时,应该选择哪个类中的该函数进行摘要提取,通过静态分析来解决这些问题是本课题研究的重点。
本文第1节对类的抽象内存表示模型进行概述;第2节介绍类的操作语义模拟算法;第3节介绍基于抽象内存模型的单元测试用例生成方法;第4节通过一个实例演示路径分析中抽象内存的变化过程;第5
节对提出的模型和算法进行实验并分析实验结果;第6节总结全文。
1 类的抽象内存表示模型
为了明晰函数中类对象的可取类型,以便确认单元测试的输入变量类型集合以及在函数调用点的函数
摘要提取操作[
13]
可以在路径生成后,通过构建类的抽象内存模型并配合符号执行技术提取出路径上与类对象有关的约束,缩小类对象实际类型的可选范围,在精简测试用例集合的同时保证覆盖率,提高单元测试的效率。
抽象内存模型是存储变量语义和约束的静态存储
介质,用于记录变量在符号执行中的动态变化[14]
对程序执行自动测试,需要先通过静态分析得到测试所需的代码信息,为此需要构建符号表,它也是类的抽象内存模型的基础。
1 1 类的符号表
在静态建模中,抽象语法树是最重要的中间结构,它是源程序的一种抽象表现,也是提取程序信息的入手点。源程序的每行代码以及每个关键字都有对
应的抽象语法树节点[15]
对面向对象程序执行单元测试需要先通过静态分析得到被测函数模块的输入变量。这就需要遍历程序的抽象语法树,正确并完整地识别出程序中各个类、函数和变量的信息以及它们之间的关联关系,将其记录于符号表中,在随后生成测试用例时便能快速获取所需信息。本课题的研究重点是类,由类在组成结构方面的特点可以归纳出其基本信息,由此可得类对应符号表的结构如表1所示。
表1 类对应符号表的属性
唐纳德
属性说明className类名parents类的父类情况children类的子类情况memVars类的成员变量memMethods类的成员函数isAbstract是否为抽象类scop
e类对应作用域
面向对象程序中不同的类可能存在于不同的继承体系中,同一继承体系中类的属性也存在差异。如果该类存在于继承体系中,就将它的父类和子类对应的
投稿网址:www.jsjcly
kz.com
  计算机测量与控制 第30
·86 ·符号表项和继承类型存储起来[16]
,由于面向对象程序的继承特性,子类无需声明便可拥有父类的成员,在记录了类的继承情况后,就可以通过符号表快速获取从父类继承下来的属性和特征。C++支持多继承;Java中的类虽然只能单继承,但是可以实现多
个接口,因此parents是一个列表[1
7]
中国武术散打功夫王争霸赛。在类的成员变量中,静态变量需要单独标注,因为它们为该类的所有实例所共享,在抽象内存模型的构建和测试用例生成中都需要单独处理。不论是通过哪个实例对静态变量进行访问,实际效果和通过类名
原云南省委书记高严调用是相同的,影响的都是同一个成员[
18
]。在类的成员函数中,构造函数、静态函数、没有函数体的函数(例如Java的抽象方法和C++的纯虚函数)、有函数体但可以重写的函数(例如Java中除构造方法、静态方法、final和private修饰的方法以外的其他方法以及C++的虚函数)以及其他函数需要分开记录,可以通过这些列表的存储情况来判断类是否为抽象类。
isAbstract用于说明该类对象能否直接实例化。因为抽象类和接口不能直接实例化,需要通过实例化子类并向上转型的方式来间接完成,所以这里需要被区分。本课题将接口与抽象类同等处理。在C++中,抽象类的子类只要没有重写父类的全部纯虚函数,就仍为抽象类;在Java中,抽象类通过abstract显示声明。
1 2 抽象内存模型分类
在面向对象程序中,针对不同的数据类型,可以将抽象内存模型分为基本抽象内存模型、数组抽象内存模型、指针抽象内存模型和类的抽象内存模型,其中类的抽象内存模型也适用于记录结构体变量的内存情况。不同类别的抽象内存模型存在一些公共属性,如符号值、抽象内存单元地址、符号表项等。除了公共属性以外,不同的数据类型还有不同的语言特性,比如数组类型需要记录数组长度、指针类型指向的是
内存单元地址、类的成员在内存中离散存放[19]。每
个变量都对应一个抽象内存模型,类对象也是如此,但是同一个类的多个对象之间共享一部分属性,即静态变量,任意对象都可以对这部分属性进行访问和修改。
本课题将类的抽象内存模型中与类结构相关的属
性提取出来,这部分属性与类本身相对应,构成类类型的抽象内存模型;剩下的属性则与具体对象有关,构成类对象的抽象内存模型。类类型的抽象内存模型与类对象的抽象内存模型之间的关系如图1所示
图1 类的两种抽象内存模型之间的关系
1 3 类类型的抽象内存模型
以Java为例,在使用某个类时,首先要将该类加载到内存中,通过类加载器创建相应的Class对象。类的静态变量在内存中仅有一份,随着类的加载在方法区中分配内存,所以类的每个静态变量的抽象内存模型也应该只有一份。在路径分析中,即使是通过不同的类实例来访问和修改静态成员,影响的也只是同一个变量。因此,为每个初次遇见的类的Class对象构建类类型的抽象内存模型,其结构如表2所示,随着路径分析建立类类型的抽象内存模型与静态变量之间的关联关系。虽然C++的这一过程与Java不同,但是在符号执行中可以同等处理。
表2 类类型的抽象内存模型属性属性说明
symbol代表Class对象的符号virtualAddr类类型的抽象内存单元地址
nameDeclaration
类对应符号表项
name类名
typeClass对象的数据类型
members
类的静态变量名和各个静态变量对应
抽象内存模型的映射关系
不同类别的抽象内存模型放在对应的抽象内存区中,并通过地址来定位。针对不同的抽象内存区,地址使用不同的前缀,基本抽象内存模型所在区的前缀为犕狀,数组抽象内存模型所在区的前缀为犕犪,指针抽象内存模型所在区的前缀为犕狆,类的抽象内存模型所在区的前缀为犕犮。 
投稿网址:www.jsjcly
kz.com
第2期
杜婉莹,等:
单元自动化测试中类的抽象内存模型研究
·87 ·
符号表项和抽象内存模型是一一对应的,被测函数中的每个类、变量以及复杂变量的成员变量都有这两项信息。为了提高运行效率,members初始为空,只有在初次遇到静态变量时,才为其创建抽象内存模型,并将映射关系添加进去,下一节中类对象的抽象内存模型同理。
1 4 类对象的抽象内存模型
在路径分析中,对于首次遇到的类对象,要为其构建类对象的抽象内存模型。类对象离不开它所属的类,每个类对象都可以访问所属类的静态成员,这就需要指向对应的类类型的抽象内存模型的指针。由于面向对象程序的多态性,类对象的所属类需要进一步分为引用所属类和实际所属类,以便对类型转换语句和函数调用点进行分析,两者的意义不同,引用所属类从指向对象的指针或引用处获取,实际所属类记录于类对象的抽象内存模型中。类对象的抽象内存模型的结构如表3所示。
表3 类对象的抽象内存模型属性属性说明
symbol代表当前变量的符号virtualAddr类对象的抽象内存单元地址
nameDeclaration
变量对应符号表项
name变量名source变量来源realCType类对象的实际所属类
members
类对象的非静态变量名和各个非静态
变量对应抽象内存模型的映射关系
name要么为变量声明时的名字,要么为复杂变量的成员变量名,如数组变量array的第一个成员变量的变量名为array[
0]。source指明该变量是输入参数、局部变量、全局变量还是类成员变量,又或者是上述某个复杂变量的成员变量。其中,输入参数、全局变量、类成员变量均是测试用例的组成部分,局部变量则不需要放在测试用例中。
realCType是类对象的抽象内存模型中最重要的属性,这是提取类型约束的关键,每个类对象的抽象内存模型都有指向所属类对应的类类型的抽象内存模型的指针。即使存在父类引用指向子类对象的情况,类对象能访问的静态成员也只与当前引用所属类有
关,不会受实际所属类影响。例如,类Apple继承了类Fruit,定义变量App
lea,将其向上转型为Fruit,此时它的引用所属类为Fruit,实际所属类为Apple,可以访问Fruit的静态成员,而无法访问App
le的特有属性。然而,类对象的实际所属类在对路径的语义模拟和可达性分析、以及作为后续测试用例生成的参考中有很大作用。
2 类的操作语义模拟算法
类对象作为非数值类型变量,在符号执行中仅仅
使用一个符号[20]
对其进行表示是不够的,也不利于使用约束求解器[
21]
对其求解,需要在路径分析中通过动态地抽象内存建模来描述它的语义和约束。使用抽象内存模型对非数值型变量的约束进行处理,从中提取出数值型约束,并将剩余部分转化为抽象内存中的存储结构。操作语义模拟算法能够在符号执行时根据某个程序点之前各个变量的语义和约束信息,以及当前语句的语义信息来更新相关变量的抽象内存模型,模拟出抽象内存中的状态变化。在符号执行中,当被测函数含有类对象时,构建类的抽象内存模型,配合操作语义模拟算法,提取出路径中的约束,对类对象的具体类型进行限定,从而生成满足路径条件的测试用例或者得到函数调用点处调用方法所属的类。与类有关的操作有对象创建、成员访问和类型转换,接下来以C++语言为例,分析每种操作
对应的操作语义模拟算法,并用形式化语言进行描述。
2 1 对象创建
如表4所示,在C++中,创建对象有两种方式———直接定义(见①)和通过指针创建(见②)。前
者与基本数据类型变量的定义格式类似,对象在栈上分配内存,不会体现面向对象程序的多态性;后者定义一个指向对象的指针,后续可以使用指针访问对象的成员变量和成员函数,对象本身是匿名的,在堆上分配内存,可能存在父类指针指向子类对象的情况,
此时会出现多态[22]
。使用new创建的对象需要配合
delete及时删除,以防止无用内存堆积。
表4 对象创建的两种方式
序号代码
①ClassNamevar
;②
ClassName p(=newClassName
); 
投稿网址:www.jsjcly
kz.com
  计算机测量与控制 第30
·88 ·对形如①的语句,直接为变量var创建类的抽象内存模型,首先判断是否已经创建了类ClassName对应的类类型的抽象内存模型,如果没有就进行构建;随后为变量var本身创建类对象的抽象内存模型,指定变量来源,变量的实际所属类为Class Name。无论是初次创建类类型的抽象内存模型还是初次创建类对象的抽象内存模型,都需要为其指定一个符号,在类的抽象内存区中新建一块抽象内存单元,并与对应的符号表项进行关联,且members初始均为空。
对形如②的语句,先为指针p创建指针抽象内存模型,指定指针来源,定义指针状态为非空,再按上述步骤为指针p指向的类对象创建类的抽象内存模型,建立两者之间的联系。综上所述,对象创建的操作语义模拟算法的形式化语言描述如表5所示。如果只是声明ClassName类的指针p,没有对其赋值,那么它的初始状态为不确定,此时暂不需要创建类的抽象内存模型;如果约束指针状态为非空,但指针指向对象的类型尚不确定,那么类对象的实际所属类为空;如果类对象的引用所属类和实际所属类不同,还要判断两者是否存在继承关系。
表5 对象创建的操作语义模拟算法
序号
算法
写给每一个自己
if((model=getClassModel(ClassName))==null
)model=createClassModel(ClassName);createObj
长泽锌
ectModel(var).setSource(source).setRealCTyp
e(model);②
createModel(p).setSource(source).setPTState(NOT_NULL)
.setPT(createModel( p
));2 2 成员访问
如表6所示,在C++中,根据创建对象方式的不同,访问成员的方式也有两种———直接访问(见③)和通过指针访问(
见④)。表6 成员访问的两种方式序号代码③var.mem④
p->m
em访问对象的mem成员时,如果已经为其构建了
抽象内存模型,则判断是否需要建立对象和成员之间的关联关系,并根据成员类型和语义信息提取约束即可,否则为其构建抽象内存模型。对形如③的语句,构建var和var.mem之间的关联关
系;对形如④的语句,构建 p和p->mem之间的关联关系。如果mem是静态变量,关联关系建立在类类型的抽象内存模型中;如果mem是非静态变量,关联关系建立在类对象的抽象内存模型中。综上所述,以形如③的语句为例,成员访问的操作语义模拟算法的形式化语言描述如表7所示。
表7 成员访问的操作语义模拟算法
序号
算法
③if((model=getModel(var.mem))==null
)model=createModel(var.mem);if(!isStatic(var.mem)
)getModel(var).putMember(model.g
etName(),model);if(isStatic(var.mem)
)getModel(var).getRealCType().putMember(model.g
etName(),model);值得注意的是,如果子类Apple继承了父类Fruit的静态成员color,在抽象内存中同时存在类Fruit的抽象内存单元和类Apple的抽象内存单元,且两个类类型的抽象内存模型的members都包含color,那么它们存储的color理应指向同一个抽象内存模型,为便于查,类的静态成员的完整变量名统一为定义所在类的类名+变量名,比如,在这个例子中,类成员color在抽象内存模型中存储的变量名为“Fruit::color
”。2 3 类型转换
类型转换是类的操作语义模拟算法关注的重点,只有当路径中存在对类对象的类型判断或者类型转换语句时,才能根据不同分支或是能使程序继续执行所需的转换条件对类对象的类型进行约束。对象的向上转型会自动完成,因为向上转型一定是安全的,但是一旦转型为父类对象,就无法再调用子类原本特有的方法;对象的向下转型需要进行强制类型转换,且必须先发生过向上转型、才能成功向下转型,否则会报错。在编译时,编译器无法判断对象实例化时传递的是什么数据类型,因此不会对强制类型转换进行 
投稿网址:www.jsjcly
kz.com

本文发布于:2024-09-22 17:22:07,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/124492.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:内存   抽象   模型   对象
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议