一种自动化测试方法与流程

文档序号:18101900发布日期:2019-07-06 11:24阅读:160来源:国知局
一种自动化测试方法与流程

本发明涉及一种测试接口的自动化测试方法。



背景技术:

现有技术中的软件接口测试方法主要有三种:

1、使用postman等工具进行单接口的测试,根据测试返回的数值进行对比。这种方法的缺点是:用户只能对一个接口进行单向测试,拿到一个接口数据之后,需要进行手动填写,再进行下一个接口的测试,非常繁琐,而且无法实现多脚本的变量互通,之前测试案例所获得的变量值,在其它案例中无法使用。

2、通过表格的形式填写url和协议参数进行线性业务接口数据测试。这种方法的缺点是:无法进行一些特殊需求的灵活测试,比如某接口要在上个接口请求五秒后处理,或者某接口要对上个请求的某个参数进行特殊的运算,等等。

3、根据现有的语言脚本编写的内部测试工具。这种方法的缺点是:现有的语言都是大而全通用语言,比如python、java等,因此对测试人员的开发水平要求较高,实现难度较大。



技术实现要素:

本发明提出了一种自动化测试方法,其目的在于:实现灵活、自定义的线性测试,降低实现难度,实现多案例件的变量互通。

本发明技术方案如下:

一种自动化测试方法,编写测试脚本和java模板文件;所述测试脚本通过调用java模板文件中的api方法进行测试;所述测试脚本中还定义有用于测试的全局变量。

作为本方法的进一步改进:编写测试脚本时,使用预设的关键字标示变量、所要执行的代码块以及代码块中所调用的api方法;

测试脚本通过脚本解释器执行,所述脚本解释器基于java开发,执行步骤为:

(1)解释所述测试脚本:通过检测关键字识别出脚本中的变量、代码块和代码块中调用的api方法;并且,建立变量栈和类栈;

(2)在内存池中为脚本中的变量分配内存并赋值,获取变量的内存地址压入到变量栈中;并且,将脚本中的代码块作为类分配内存,获取代码块的内存地址压入到类栈中,类栈中每个代码块还分别对应一个链表,链表中的元素与该链表所对应的代码块中的api方法一一对应,链表的顺序与代码块中api方法的执行顺序一致;所述链表的元素中存放了执行所对应的api方法时所需要的信息,该信息包括调用api方法时需要访问的变量的内存地址;

(3)类栈中的各代码块依次出栈,各代码块出栈时根据所述链表依次通过java反射机制调用java模板文件中调用的api方法,并在执行api方法时通过对应的链表元素中的变量内存地址访问变量。

作为本方法的进一步改进:所述java模板文件包括本地模板文件和用户自定义模板文件;所述本地模板文件被包含在脚本解释器项目内部;

如果测试脚本需要调用用户自定义模板文件中的api方法,则:通过javac编译器对用户自定义模板文件中的api方法进行编译,获取编译后的class字节码,再将class字节码中类索引对应的常量池位置中的类完全限定名修改为虚构的脚本解释器内部文件路径,以使jvm加载器将用户自定义的api方法识别为脚本解释器项目内部的api方法从而实现正常加载;将修改后的class字节码压入jvm虚拟机,获取对应的class对象,再将class对象放入全局缓存中;步骤(3)中调用api方法时,通过反射机制扫描本地模板文件和上述全局缓存中的class对象,找到相应的api方法进行动态执行。

作为本方法的进一步改进,通过关键字进行识别的步骤为:

对于某一关键字,首先,将该关键字的信息载入内存,所述关键字的信息包括关键字值w和该关键字的长度l,同时定义一个初始值为0的整数t用于记录当前已检测出的、与该关键字的值w相连续符合的字符的长度;

然后,在顺序读取测试脚本时,每读取一个字节的ascii码,针对该关键字进行如下判断:根据t计算关键字中当前待检测的字符的位置f,然后取出关键字值w中f所指向的待检测字母x,判断当前读取到的ascii码与字母x的值是否相等:如果不相等则将t重置为0,然后读取下一个字节的ascii码进行验证;如果相等则将t的二进制值中为0的最低位设置为1,并判断t的二进制中为1的位数与l是否相等,如果相等则判断检测到关键字,如果不相等则继续读取下一个字节的ascii码进行判断;

检测到关键字后,依据关键字在测试脚本中的位置以及预设的标示格式获取该关键字所标示的信息。

作为本方法的进一步改进:根据t计算关键字中当前待检测的字符的位置f的具体方法为:f=(int)(math.log(t+1)/math.log(2))。

作为本方法的进一步改进:所述取出关键字值w中f所指向的待检测字母x的具体方式为:根据f对关键字值w进行移位操作、将待检测字母x移至最低位,然后将移位后的w与0xff做与运算,得到f所指向的待检测字母x。

相对于现有技术,本发明具有以下积极效果:(1)本方法提供一种简单、灵活、使用门槛低的脚本及其解释器,同时可以将需要重复使用的功能写入模板文件,测试人员只需要关心测试的输入、输出以及中间的过程跳转,动态调整相应参数,即可完成测试,灵活性强,使用方便;(2)本方法提供的脚本、解释器及模板文件,还可以扩展成为一个工具平台,实现更多的功能,具有极强的衍生扩展性;(3)脚本变量为全局变量,之前测试案例所获得的变量值能够在其它案例中访问;(4)提供模板文件,用户可以实现自定义的数据处理办法,供脚本直接调用,同时进一步地通过篡改class字节码,突破java加载器的限制,使客户自定义的api方法能够被正常调用;(5)由于采用脚本式测试,因此可以自由搭配及顺序执行测试案例,同时保证案例留底。

附图说明

图1是本方法的流程示意图。

具体实施方式

下面结合附图详细说明本发明的技术方案:

如图1,一种自动化测试方法,步骤为:

一、编写测试脚本和java模板文件;所述测试脚本通过调用java模板文件中的api方法进行测试;然后所述测试脚本中还定义有用于测试的全局变量。

编写测试脚本时,使用预设的关键字标示变量、所要执行的代码块以及代码块中所调用的api方法。

本实施例的脚本节选如下:

$url=http://10.1.0.31:8090/api/account/logincheck;#登录地址

$url2=http://10.1.0.31:8090/api/userinfo/getwebstulist;#select班级信息地址

$url3=http://10.1.0.31:8090/api/userinfo/modify;#修改关系地址

$param={"loginname":"13999999999","client":"web","pwd":"sih+9quk+on1xtnay+iutq==","type":"2"};

$t;

#json变量拼接若变量为数字类型则要加%符号,例如:$ok={"tem":%($param.type)};字符串类型则无需加此符号

$ok={"tem":($param.type)};

#为注释

#目前官方api模板:

#test(a)backa;打印并返回原值

#post(url,param,t)backa;发送post请求,第一个参数为url,第二个参数为postbody携带内容

#eq(a,b)断言两者是否相等,若不相等本脚本停止继续执行并输出断言失败行数

methodtest1:{

do->test(ok)backok;

#发送post请求

do->post(url,param,t)backparam;#登陆请求返回值覆盖在param变量

#将返回值中的schoolid保存到一个变量school里

$school=$param.data.schoollnfo.schoolid;

#继续拼接使用school变量

$ch={"count":true,"page_index":1,"page_size":20,"search":{"searchtype":"1","userdep":[],"usernamelike":"","schoolid":($school)}};

#保存token返回值

$t=$param.data.token;

#执行下一个模块

go->test2;

}

methodtest2:{

do->post(url2,ch,t)backparam;

$stu={"schoolid":($school),"userserial":7,"userdep":100322,"userdepname":($param.data.records[0].userdepname),

"username":($param.data.records[0].username),

"parentlnfo":[{"parentname":($param.data.records[0].family_name),"parenttele":"15212345678","parentld":6}],

"schoolid":($school),

"userdep":100322,

"userdepname":($param.data.records[0].userdepname),

"userlnfo":{"userrela":1},

"username":($param.data.records[0].username),

"userno":"10000",

"userphoto":"15458733030.b7fac1g2568.gif",

"userserial":7,

"usertype":"1"};

do->post(url3,stu,t)backparam;

$m=$param.code;

sn=600;

……

脚本中,通过#标示注释,$标示变量,methodtest{……}标示代码块,go->method标示代码块之间的跳转,do->method(……)标示调用java模板中的api方法。

二、测试脚本通过脚本解释器执行,所述脚本解释器基于java开发,执行步骤为:

(1)解释所述测试脚本:将脚本编辑好的脚本的ascii码压栈、出栈,根据本脚本自定义的语法,通过检测关键字识别出脚本中的变量、代码块和代码块中调用的api方法;并且,建立变量栈和类栈。

上述通过关键字进行识别的步骤为:

对于某一关键字,首先,将该关键字的信息载入内存,所述关键字的信息包括关键字值w和该关键字的长度l,同时定义一个初始值为0的整数t用于记录当前已检测出的、与该关键字的值w相连续符合的字符的长度。

假设需要识别的关键字是back,其含义是将值返回,大致等同于return。通过查找ascii对照表得知,intb=0x62,inta=0x61,intc=0x63,intk=0x6b;并且back由四个字母构成,所以长度l为4。设定该关键字信息的常量值如下:

publicstaticfinalintback_lenght=4;//长度为4。

longback=k<<(8*3)|c<<(2*8)|a<<8|b;//即上述关键字值w。注意是字母反向顺序,因为io的读取顺序是不同的。

构建一个实体类存放关键字信息:

publicclasskeywordbox{

privatelongkeyword;//即上述w

privateinttest;//当前已检测正确的长度,默认0,即上述t。

privateintkeylen;//该关键字长度l。

在解释器启动之前,我们通过构造函数将关键字信息加载进内存:

publickeywordbox(longkeyword,intkeylen){

//关键字常量,例如longback的值。

this.keyword=keyword;

//关键字长度,例如back_lenght=4;

this.keylen=keylen;

//当前已检测正确的长度,默认0。

this.test=0;

}

}

然后,在顺序读取测试脚本时,每读取一个字节的ascii码,针对该关键字进行如下判断:根据t计算关键字中当前待检测的字符的位置f=(int)(math.log(t+1)/math.log(2)),然后取出关键字值w中f所指向的待检测字母x:根据f对关键字值w进行移位操作、将待检测字母x移至最低位,然后将移位后的w与0xff做与运算,得到f所指向的待检测字母x,再判断当前读取到的ascii码与字母x的值是否相等:如果不相等则将t重置为0,然后读取下一个字节的ascii码进行验证;如果相等则将t的二进制值中为0的最低位设置为1,并判断t的二进制中为1的位数与l是否相等,如果相等则判断检测到关键字,如果不相等则继续读取下一个字节的ascii码进行判断。

检测到关键字后,依据关键字在测试脚本中的位置以及预设的标示格式获取该关键字所标示的信息。

检测识别的程序实现及相关解释如下:

publicvoidkeyword(filereadread,byteb,keywordboxbox){//关键字检测

//第一个参数是io读取回调类。

//第二个参数io这次读取的ascii字节码也就是一个byte。

//第三个参数就是我们刚刚启动时候加载的记录关键字信息的实体类。

//第一步我们将这三个值都取出来,这三个值是我们解释器启动之前就初始化加载进来的。

inttest=box.gettest();//对应方法中所述t。

longkeyword=box.getkeyword();//对应方法中所述w。

intkeylen=box.getkeylen();//对应方法中所述l。

/*第二步,详细解释如下:

变量f对应前述方法中的f,是算得当前要检测的字符在关键字back中所处的位置。

test对应前述t,是通过二级制的数值记录已经检测过的长度值,每检测到一个字符,将最低位的0置1。

test初始值为0,开始检测的时候,当io传来一个字节的ascii码,如果其值为0x62(恰好是b的ascii值),可以认为这可能是back关键字的开始。此时将test的最低位的0置1,即test=00000001。然后io传来第二个字节的ascii码,如果此值为0x61也就是a的ascii值,可以判定已经连续匹配了两个字母,此时test=00000011,也就是将为0的最低位变为1,其十进制数值为3。

如果io继续传来的ascii码不是c,说明与关键字不符,将test置零:test=00000000。当test=00001111时,说明back关键字完全匹配,此时触发回调函数,通知相关线程关键字back已出现了。

由于test是连续个1构成的二进制数值,因此要根据test确定当前要检测第几位f。可以将其+1,结果即与2^f相等。

因此要通过求以2为底、test+1的对数来求取f,但是java提供的对数api只有自然数e为底的对数计算api:math.log(n)=log(e)(n),因此需要通过换底公式:log(a)(b)=log(c)(b)/log(c)(a)进行转换,最终得到下式:

intf=(int)(math.log(test+1)/math.log(2));

计算得到f之后,需要继续获取这个位置上的字符:已知keyword=k<<(8*3)|c<<(2*8)|a<<8|b,同时可知待检测字母x在常量中所处的位置是x<<8*f,因此可以移位keyword>>(8*f),将小于x的字母先挤掉,让x常量出现在最低位,然后&0xff,因为8个字节最大值就是0xff,所以x与0xff进行与运算必然可以将x所属常量的八个字节拿出来,然后判断两者是否相等。实现代码如下:*/

longm=(keyword>>(8*f))&0xff;

if(b==m){

//通过移位及或运算为test中为0的最低位置1。

test=test<<1|1;

if((int)(math.log(test+1)/math.log(2))==keylen){

//关键字检测。

//若检测成功后长度等于关键字长度,则关键字出现。

if(read!=null){

//触发关键字检测,成功回调。

read.keyhere(keyword);

}

//将已检测到的长度值重新变成0。

test=0;

}

}else{

//检测失败,并非关键字,将已检测到的长度值重新变成0。

test=0;

}

//将计算后的新test保存到内存中。

box.settest(test);

box.setkeyword(keyword);

box.setkeylen(keylen);

}

检测算法流程完毕。

由于标示的格式是预先设定好的,因此检测到关键字之后,可以根据格式轻易地获取脚本中相关信息,确定该处所要执行的行为是什么,执行的对象是什么,等等。

(2)如图1,在内存池中为脚本中的变量分配内存并赋值,获取变量的内存地址压入到变量栈中;并且,将脚本中的代码块作为类分配内存,获取代码块的内存地址压入到类栈中,类栈中每个代码块还分别对应一个链表,链表中的元素与该链表所对应的代码块中的api方法一一对应,链表的顺序与代码块中api方法的执行顺序一致;所述链表的元素中存放了执行所对应的api方法时所需要的信息,该信息包括调用api方法时需要访问的变量的内存地址。

链表元素的实体类结构如下:

publicclassmethodspace{

privatelongbackvar;//方法返回值内存地址

privatelist<long>list;//方法参数名称内存地址列表

privatestringmethodtyep;//方法对应模板映射

privateintdotype;//执行类型

privatestringvarname;//变量的赋值表达式

privatestringclassname;//执行类的名称

privateinthang;//行数}

(3)类栈中的各代码块依次出栈,各代码块出栈时根据所述链表依次通过java反射机制调用java模板文件中调用的api方法,并在执行api方法时通过对应的链表元素中的变量内存地址访问变量。

反射调用的具体方式为:出链表时,按照元素中实体类里面的参数数值,修改内存中变量对应值,如果出现脚本语法错误或者其他业务报错,可以根据代码行数抛出错误。

反射调用的具体方法为:解释器启动时将根据链表中的publicclassmethodspace记录的执行方法来执行,参数的数量及类型根据java反射获取,将参数和执行语句拼接成string字符串,然后根据这个拼接的执行字符串反射执行模板中的api方法。

进一步的,所述java模板文件不仅可以包括本地模板文件,还可以包括用户自定义模板文件。

所述本地模板文件被包含在脚本解释器项目内部,脚本解释器运行时可以正常调用。

但对于java提供给用户的类加载器,若.class索引的地址不是在本项目内,则拒绝加载,所以用户自定义的模板文件是不能被java类加载器正常加载的。为了实现api方法的灵活自定义,突破java机制的限制,进一步采用如下步骤。

如果测试脚本需要调用用户自定义模板文件中的api方法,则:通过javac编译器对用户自定义模板文件中的api方法进行编译,获取编译后的class字节码,再将class字节码中类索引对应的常量池位置中的类完全限定名修改为虚构的脚本解释器内部文件路径,以使jvm加载器将用户自定义的api方法误认为是脚本解释器项目内部的api方法,从而实现正常加载;将修改后的class字节码压入jvm虚拟机,获取对应的class对象,再将class对象放入全局缓存中,从而可以让脚本映射调用。步骤(3)中调用api方法时,通过反射机制扫描本地模板文件和上述全局缓存中的class对象,找到相应的api方法进行动态执行。通过该方式,无论用户自定义的api方法是在本地的磁盘中,还是在外网或内网服务器中,都可以被脚本调用。

可以根据测试需求同时执行多个脚本,脚本中的变量可以互相访问,所有脚本执行结束后,变量将被释放。在脚本执行过程中,有任何异常,包括不限于语法错误、断言不通过、网络响应、变量不存在该属性等问题,都会在可视化组建的控制台上打印并报错误行数、耗时和每个脚本的执行与成功与否等信息。

当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1