3、基于协作图生成集成测试用例的方法
3.1研究假定
为了有针对性地解决从协作图生成测试用例的问题,本文作出如下假定和要求:
(1)假定协作图描述的协作与用例图描述的规约是一致的。模型本身的验证是通过非形式化的复审和形式化的模型检验方法进行的,已超出本文的研究范围;如果协作图上的场景路径集不能覆盖所有消息,则说明协作图本身有错误;
(2)假定系统中的对象都是自行开发的,不包括第三方组件对象,文中的对象可以是不同粒度的对象(类的实例、类簇、组件、子系统、系统),也就是说我们在分析任意对象或类时,在必要的情况下都可以获取其规约和内部详细设计信息,包括功能和结构信息;对于第三方组件的集成测试,[13][17]介绍了详细的测试方法;
(3)我们研究的测试是针对检错进行的,确认软件是否正确实现了设计,协作图上的结构关系是否被正确实现,[15]中已有相应的静态检验方法,所以本文只研究动态交互行为的测试;
(4)本文为了明确解决问题,假定消息类型只有普通消息、条件消息、循环消息,而且只存在顺序循环,不存在嵌套循环。
3.2方法概述
基于协作图的集成测试(Collaboration diagram-based Integration Testing, CIT )方法集合了传统的白盒测试方法和黑盒测试方法,用于测试协作图中参与协作的对象之间通过消息的交互,对每个协作图处理一次,得到相应的测试用例集。首先分析协作图,提取协作图中表示的所有元素,根据消息的顺序号和消息的条件,找到每一条消息的直接后继消息,然后根据场景路径的定义,使用深度优先方法遍历消息及其直接后继直至到达无直接后继的消息从而生成场景路径,然后回溯到没有被访问的直接后继,重复上述方法找到所有的场景路径;在访问消息获取场景路径的同时,获取该路径的方法调用序列、参数和路径条件,将这些集成测试的关键因素用范畴-划分方法定义为方法序列、环境条件、系统输入、系统输出等范畴,结合该协作片断的用例规约和类图中的定义生成这些范畴的可能选择,然后结合路径约束条件在这些范畴的划分中确定选择项的合理组合,这样我们就等到了该场景路径完整的测试用例,包括外界输入、交互输入、预期方法调用序列、后条件、预期输出。对协作图中的所有场景路径都构造了测试用例,就形成了协作集成测试用例集。这样在实际执行集成测试时不但可以直接观察到系统级的输入作用下协作实现过程中的实际输出,还能够通过动态插装方法在代码中加入不影响软件功能的观察代码[15],使测试人员能够观察到实际协作执行时的方法调用序列和数据流的定义和使用,然后通过比较最终系统实际执行时输出与预期输出的一致性决定该协作实现的功能是否正确,通过比较应该发生的方法调用序列和实际执行时观察到的方法调用序列是否一致,确定协作表示的交互行为是否正确,从而从整体上对协作图描述的系统行为的实现是否与设计一致,从而完成协作的集成测试。
本方法可以以增量的方法进行,最终生成的测试用例的具体程度,与相应UML模型图的设计精化程度相关,因为协作图描述的场景的详细程度与测试用例的表达能力直接相关。
3.3 UML协作图生成测试用例的算法描述
本文的方法能够从协作图规约文档直接生成测试用例,主要描述分析协作图规约文档提取集成测试需求信息并生成消息后继表的算法UMLspecificationparser(),然后为每个消息确定其直接后继消息的findsuccessor()算法,根据消息后继表遍历所有场景路径获取测试用例规约信息的spathgenerator()算法,以及从测试用例规约使用范畴划分方法确定测试用例输入值、预期输出值、预期输出行为的算法testcasegenerator()。UMLspecificationparser()算法从协作图从获取集成测试需求信息是通过对UML协作图规约的文本文件(如Rational Rose 的MDL文件)进行分析完成的[11],在我们的工具中实现相应的功能,本文对分析算法不作详细描述。我们用邻接表定义消息后继列表,将分析结果信息按消息顺序号升序记录到messagelist的表头中,以便下一步处理。我们定义消息后继列表如下:
messageitem{
mid:string;//消息顺序号
mguardcondition:string;//消息卫式条件表达式
mlabel:string;//消息标签
msender,mreciever:object;// 消息发送者对象,消息接收者对象
mmethod:method;//消息激活的方法
mtype:[flat flow, procdure call, call return];// 根据消息的箭头及线型划分的消息类型
link:pointer of msuccessor;
}
msuccessor{
mid:string;//后继消息顺序号
mscondition:string;//选择该后继的条件表达式
next:pointer of msuccesor
}
messagelist:messageitem[n];
findsuccessor()算法为消息生成直接后继列表的并记录到消息的后继链表中,描述如下:
findsuccessor(messagelist){ //找到所有消息的所有可能后继和每个后继条件
//输入:消息后继表messagelist
//输出:消息后继表messagelist
//出口准则:所有消息都处理完
for i:=1 to n do {
if i= =n 或者messagelist[i]为端口输出事件触发消息
messagelist[i].link=nil;i:=i+1; //最后消息或触发端口输出事件的消息无后继消息
else{
if messagelist[i+1]是普通消息
则messagelist[i]的直接后继为messagelist[i+1];
else{
k:=1;
do while messagelist[i+k]是条件消息{
messagelist [i+k]是messagelist [i]的直接后继,且条件为messagelist [i+k]的条件取真
if messagelist [i+k]是循环或嵌套层次的第一个消息{
t:=该层次的消息数;messagelist [i+k+t]是messagelist [i]的直接后继;
条件为messagelist [i+k]的条件取假; k:=k+t;}//条件取假时循//环或嵌套内的消息都不发送
else{
messagelist [i+k+1]是messagelist [i]的直接后继;
条件为messagelist [i+1]的条件取假; k:=k+1;//不发送
}endif
}enddo
}endif
i:=i+1;
}endif
}endfor;
}endfindsuccessor;
spathgenerator()通过访问消息后继表中消息及其直接后继生成场景路径,在遍历场景路径时同时记录路径上相应的控制流和数据流信息,执行完毕得到一个场景路径集spath相应的控制流数据流测试规约。其算法描述如下:
s-pathgenerator(m:msuccessorlist){
//输入:messagelist; (消息后继表)
//输出:spath;(场景路径集)
spath{
sid:integer;//场景路径顺序号
pathcondition: list of gardcondition; //场景路径的实现条件
mlist:list of message;//场景路径上的消息流列表
parameters:list of variable;//场景路径上定义和使用的变量列表
userinputparameters: list of variable; //场景路径上外界输入参数列表
methodsequence:{objectname,methodname,next};场景路径上由消息激活的方法序列
}[n];
i,j:integer;
i:=1; j:=1;
Sid:=j;//场景路径序号
//递归访问消息及其直接后继
访问消息m,在消息标签中取出并记录返回值、参数、调用的对象方法;
记录消息条件到路径条件中;
if m的后继消息为空
{ 一条场景路径结束;j:=j+1;return;}//回溯到m[i]的直接前驱继续
else{
k:=1;
while m.link<>nil do { //m的后继消息列表不空
successor:=m.link;
spathgenerator(successor) //深度优先遍历该消息及其后继消息
m.link:=successor->next;//下一后继消息;
}endwhile
}endif;
}endfind;