软件测试工程师应该都知道,LoadRunner在录制结束后会自动生成一段脚本。这段脚本虽然很简单,但很实用,适合初学者学习。但是在真正进行项目性能测试时,只靠LoadRunner自动生成的脚本还是不够,很难达到业务的要求。因此,在录制脚本结束后,要对脚本进行完善,使其能达到业务模拟的要求,这样尽可能地使虚拟用户模拟时更接近用户的实际使用。
一、检查点
在进行压力测试时,经常会有页面间数据传递的操作。如果在测试过程中传递数据的次数逐渐增多,页面就有可能会发生传递混乱,或者客户端与服务器端数据传输被中断、传输过程中产生了错误的数据等情况。为了判断数据传递的正确性,更重要的是为了节省人工检查的步骤和时间,LoadRunner提供了在脚本中插入检查点的方法,在每次运行时都检查服务器返回页面的信息是否正确,这样可以大大提高测试效率。
检查点的原理是通过检查点函数将返回值的结果反映在Controller的状态面板上和Analysis统计结果中。这个原理是基于LoadRunner中很多的API函数的返回值会改变脚本的运行结果。比如,检查点函数web_find,如果它检查到的结果为空,它的返回值就为LR_FAIL,这样整个结果置为FAIL;反之,检查到的结果为成功,则web_find返回值是LR_PASS,整个结果置为PASS。
1、为什么需要插入检查点
通常在测试过程中使用到两种检查点,文本检查点和图片检查点。那么为什么需要插入检查点呢?以飞机订票系统的登录功能为例,我们来观察在不插入检查点回放,事务结束状态的情况。
首先录制登录的脚本,脚本如下(部分核心代码):
web_reg_save_param("WCSParam2",
"LB/IC=userSession value=",
"RB/IC=>",
"Ord=1",
"Search=Body",
"RelFrameId=1",
LAST);
web_url("nav.pl",
"URL=http://127.0.0.1:1080/WebTours/nav.pl?in=home",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/welcome.pl?signOff=true",
"Snapshot=t7.inf",
"Mode=HTTP",
LAST);
web_concurrent_end(NULL);
web_url("mer_login.gif",
"URL=http://127.0.0.1:1080/WebTours/images/mer_login.gif",
"Resource=1",
"RecContentType=image/gif",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home",
"Snapshot=t8.inf",
LAST);
lr_start_transaction("Login");
lr_think_time(14);
web_submit_data("login.pl",
"Action=http://127.0.0.1:1080/WebTours/login.pl",
"Method=POST",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home",
"Snapshot=t9.inf",
"Mode=HTTP",
ITEMDATA,
"Name=userSession", "Value={WCSParam2}", ENDITEM,
"Name=username", "Value=test1", ENDITEM,
"Name=password", "Value=1", ENDITEM,
"Name=JSFormSubmit", "Value=off", ENDITEM,
"Name=login.x", "Value=46", ENDITEM,
"Name=login.y", "Value=6", ENDITEM,
LAST);
web_concurrent_start(NULL);
web_url("nav.pl_2",
"URL=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/login.pl",
"Snapshot=t10.inf",
"Mode=HTTP",
LAST);
web_url("login.pl_2",
"URL=http://127.0.0.1:1080/WebTours/login.pl?intro=true",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/login.pl",
"Snapshot=t11.inf",
"Mode=HTTP",
LAST);
web_concurrent_end(NULL);
在这个脚本中并没有插入检查点,回放脚本,日志文件如图6-1所示。
官网01
图6-1 脚本回放日志
从回放日志中可以看出,登录事务是正确的,用户成功登录,这没有问题,现在将web_submit_data函数用户名和密码的参考修改一下,将其修改为如下代码:
web_submit_data("login.pl",
"Action=http://127.0.0.1:1080/WebTours/login.pl",
"Method=POST",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home",
"Snapshot=t9.inf",
"Mode=HTTP",
ITEMDATA,
"Name=userSession", "Value={WCSParam2}", ENDITEM,
"Name=username", "Value=abcdef00133", ENDITEM,
"Name=password", "Value=ddaa", ENDITEM,
"Name=JSFormSubmit", "Value=off", ENDITEM,
"Name=login.x", "Value=46", ENDITEM,
"Name=login.y", "Value=6", ENDITEM,
LAST);
重新回放脚本,回放日志如图6-2所示。
图6-2 脚本回放日志
从回放日志中可以清晰的看到,登录还是成功的,但实际情况下使用用户名为abcdef00133,密码为ddaa是不可能登录成功的。从LoadRunner的Run-Time Viewer视图中也可以看出,Run-Time Viewer视图结果如图6-3所示。
图6-3 Run-Time Viewer视图
登录成功后正常的情况是,Welcome后面会显示出登录的用户名。
这个例子说明一个问题,当事务的结束状态为成功时,并不能说明业务真的做成功了,那么同理,当在分析器中看到事务的成功率为100%时,平均事务响应时间也是在期望范围内时,也不能说明系统没有问题,也有可能出现这种情况,业务没有做成功,但事务的结束状态都被置为成功了,这样分析到的结果就完全错了。
那么LoadRunner是如何确定事务结束状态为PASS的呢?其实LoadRunner本身无法实际去判断业务是否做成功,其判断事务是否成功的依据是结束事务函数(lr_end_transaction)是否被执行,如果结束事务函数执行成功,那么LoadRunner则会将事务的结束状态设置为PASS。
从这个例子可以看出,如果需要确定事务是否成功,其实首先应该判断是否登录成功,只有当登录成功时,才能确定登录事务是成功的,所以其实检查点的根本目的是验证测试过程中的步骤是否被正确的执行,也可以理解为业务是否被正确的执行,只有这样才能保证后期分析的数据是正确的。
2、插入检查点
插入检查点的方法,在工作原理上是在VuGen中插入Text/Image检查点。插入检查点的步骤如下:
1)将视图模式设置为Tree View,如图6-4所示。
VuGen中包含Tree View(树视图)和Script View两种视图模式。一般情况下都是使用Script View,但在插入检查点时,一般都将视图模式切换为Tree View,这样方便找到要插入检查点的页面,对于初学者来说这是一种很好的方式,这样可以保证插入检查点的位置正确。
图6-4 切换至Tree View视图
2)在Tree View中选择要插入检查点的位置。
选中要插入检查点的位置后,点击鼠标右键,选择检查点插入的方式,可以选择插在前面,也可以选择插在后面,如图6-5所示。
3)选择检查点类型和插入函数。
插入检查点有文本检查点(Text Check)和图片检查点(Image Check)两种,选择的检查点函数也有两个,分别为:web_find和web_reg_find。
插入文本检查点(Text Check),使用web_find检查点函数的情况,如图6-6所示,点击OK按钮后会弹出Text Check Properties对话框,如图6-7所示。
图6-5 插入检查点位置
图6-6 选择检查点类型和检查点函数
在Specification选项卡,设置检查参数信息,主要有以下三个属性需要设置:
Search for:设置要检查的字符串,可以点击 对检查的内容进行参数化。
Right of:设置右边界值,也就是设置要检查字符串右边的内容,如How are you,如果要检查的内容为“are”,那么右边界值为“you”,也可以设置为空。
Left of:设置左边界值,也就是设置要检查字符串左边的内容,如How are you,如果要检查的内容为“are”,那么左边界值为“How”,也可以设置为空。
图6-7 检查点属性设置
然后切换到General选项卡,如图6-8所示,在Step Name文本框中输入该操作的步骤名称,命名时名称最好能反映该操作要搜索的对象。
图6-8 设置检查点的名称
插入文本检查点(Text Check),使用web_reg_find检查点函数的情况,如图6-9所示,点击OK按钮弹出Find Text对话框。
图6-9 检查点类型与检查点函数选择
在Find Text对话框中设置搜索的属性配置,如图6-10所示。
图6-10 检查点属性设置
在Find Text对话框设置检查点属性时,有以下几个需要注意的地方:
1)Search for specific Text:和web_find检查点函数一样,同样需要对待检查的字符串进行设置,也可以对检查点的内容进行参数化,但web_reg_find检查点函数还可以对检查的内容进行是否区分大小写、是否为二进制数据、是否使用“#”代替任意阿拉伯数字和是否使用“^”作为通配符进行设置。
2)Search in:设置查找范围,关于查找范围为三个选项:All、Body和Headers,缺省值为Body。
那么如何来确定查找范围呢?以飞机订票系统登录成功,如图6-11所示。
图6-11 登录成功
那么在使用这种方式来插入检查点时,需要检查的用户名(test1)应该是在哪里查找呢?在检查点函数web_reg_find中可以看到,通常有三种范围选择,那么在实际测试过程中可以依次在这三种范围中进行查找,待检查用户名所在的地方就是需要设置的范围。
在树模型中找到登录成功的页面,并单击HTTP View选项卡,在该选项卡中可以看到录制的过程LoadRunner所抓到客户端向服务器发送的请求和服务器向客户端返回的请求。
在Response Body和Headers选项卡中依次查找待检查的用户名(test1),该实例在Response Body中可以查找到待检查的用户名,如图6-12所示。
图6-12 查找待检查的内容
这样检查范围就应该设置为Body,那么一般在实际测试过程中待查找的内容都会在Body中,所以缺省值为Body。
3)Save count:定义查找计数器变量名称,该变量会自动统计检查内容出现的次数。
在检查点类型和函数中,选择检查点类型为Image Check,检查点函数为web_image_check,如图6-13所示。
图6-13 选择检查点类型和检查点函数
点击OK按钮,弹出Image Check Properties对话框,在该对话框中设置图片的提示信息和图片的相对路径,如图6-14所示。
图6-14 Image检查点属性设置
如果Web窗体中包含有JavaScript脚本,那么在Tree View中显示可能会有问题,这时要在General Options中进行设置,如图6-15所示。
图6-15 选择General Options
进入General Options对话框,如图6-16所示,切换到Correlation选项卡,选中Enable Scripting and Java applets on Snapshots viewer复选框,点击OK按钮,设置完毕。
图6-16 General Options设置
4)参数化。检查点也可以进行参数化,因为检查点的内容可有会出现经常变化的情况,因此在一定的时候需要对检查点的内容进行参数化操作。在检查点属性设置对话框中点击 按钮,弹出参数设置对话框,如图6-17所示。
图6-17 检查点参数化
3、检查点函数
常用的检查点函数有web_find()和web_reg_find()两个。
1、web_find()函数
该函数作用是在页面中查找相应的内容。常用参数的含义如下:
web_find("Text Check", //检查点步骤名称
"RightOf=Go to", //定义查找字符串右边界
"LeftOf=page", //定义查找字符串左边界
"What=Home", //定义检查字符串内容
LAST);
使用该函数时要注意以下几个问题:
1)该函数只能对基于HTML模式录制的脚本进行查找。
2)该函数必须在页面内容显示出来以后才能进行查找,所以该函数必须写在查找内容所在页面的后面。
3)必须启用内容检查选项,在Run-time Settings→Preferences里面,把Enable Image and text check复选框选中,否则不执行该查找函数,如图6-18所示。
图6-18 启用内容检查选项
4)在VB和Java语法中不支持该函数。
该函数存在下面两个缺点:
1)执行效率较低。
2)不返回查找结果情况。例如,要查看有多少个虚拟用户登录成功的情况,这个函数无法做到,必须进一步操作才能实现。
2.web_reg_find()函数
该函数是在缓存中查找相应的内容,是一个注册函数,常用参数及含义如下:
web_reg_find("Text=Welcome", //定义要查找的内容
"SaveCount=Welcome_Count", //定义查找计数变量名
"Search=Body", //定义查找范围
LAST);
该函数必须写在要查找内容的请求之前,一般情况下都会写在如下六个函数之前:Web_custom_request()、web_image()、web_link()、web_submit_data()、web_submit_form()、web_url()。
SaveCount参数用来记录在缓存中内容被查找到的次数,因此在实际应用中经常会使用这个参数来统计查找成功的次数,进而来判断欲查找的内容是否真的被查找到。
下面是一个实例:
web_reg_find("Text=Welcome",
"SaveCount=Welcome_Count",
LAST);
web_submit_form("login.pl",
"Snapshot=t2.inf",
ITEMDATA,
"Name=username", "Value=jojo", ENDITEM,
"Name=password", "Value=bean", ENDITEM,
"Name=login.x", "Value=35", ENDITEM,
"Name=login.y", "Value=14", ENDITEM,
LAST);
if (atoi(lr_eval_string("{Welcome_Count}")) > 0){
lr_output_message("Log on successful.");
} //判断如果计数变量Welcome_Count值大于0,则在日志中输出登录成功
else{
lr_error_message("Log on failed"); //反之则在日志中输出登录失败
return(0);
}
web_find()和web_reg_find()虽然都是检查点函数,但两个函数还是有区别的,主要区别有以下几点:
1)两个函数类型不同,web_find只是一个普通函数,而web_reg_find是一个注册函数。
2)web_find函数使用时必须开启内容检查选项,而web_reg_find函数没有此限制。
3)web_find函数录制时只能基于HTML模式录制的脚本中,而web_reg_find函数没有此限制。
4)web_find函数是在返回的页面中进行内容查找,web_reg_find函数是在缓存中进行查找。
5)web_reg_find函数在执行效率上要比web_find函数高。
4、通过检查点判断事务结束状态
前面介绍了检查点的目的是验证业务是否成功,那么如何使用检查点来验证业务成功与否呢?需要验证业务是否成功即是需要通过检查点来确定事务结束状态。
下面是检查点函数的参数格式:
web_reg_find("Text=Welcome", //定义要查找的内容
"SaveCount=Welcome_Count", //定义查找计数变量名
"Search=Body", //定义查找范围
LAST);
其函数中有一个很重要的参数SaveCount,这个参数是用于统计待检查数据被查找到的次数,如果需要确定业务是否成功,那么则待检查的数据至少要被查找到1次,这样才能保证业务被正确的处理。如飞机订票系统的登录功能,需要确定登录成功,那么至少需要在登录成功页面中能查找到登录的用户名(至少需要查找到一次),这样才能保证登录的业务是成功的。
以上面的飞机订票系统的登录功能为例,首先插入检查点,插入检查点的脚本如下:
web_reg_find("Search=Body",
"SaveCount=no", //将检查到的内容(test1)的次数保存在变量no中
"Text=test1", //需要检查的内容为test1
LAST);
接下来需要判断变量no的值是否大于或等于1,如果大于或等于1,那么将登录事务的结束状态设置为LR_PASS,反之将结束状态置为LR_FAIL,修改后的代码如下:
//通过lr_eval_string函数读取变量no的值,由于lr_eval_string函数
//读取的值为字符型,所以需要将它转换为整型,这样才能进行比较
if (atoi(lr_eval_string("{no}"))>=1) {
lr_end_transaction("Login", LR_PASS);
}
else{
lr_end_transaction("Login", LR_FAIL);
}
其中变量no的值为大于或等于1,因为在有些情况下,可能查找到的值不只一个,所以一般情况下判断其是否大于或等于1。
修改后完整的代码如下:
web_submit_data("login.pl",
"Action=http://127.0.0.1:1080/WebTours/login.pl",
"Method=POST",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home",
"Snapshot=t9.inf",
"Mode=HTTP",
ITEMDATA,
"Name=userSession", "Value={WCSParam2}", ENDITEM,
"Name=username", "Value=test1", ENDITEM,
"Name=password", "Value=1", ENDITEM,
"Name=JSFormSubmit", "Value=off", ENDITEM,
"Name=login.x", "Value=46", ENDITEM,
"Name=login.y", "Value=6", ENDITEM,
LAST);
web_concurrent_start(NULL);
web_url("nav.pl_2",
"URL=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/login.pl",
"Snapshot=t10.inf",
"Mode=HTTP",
LAST);
web_reg_find("Search=Body",
"SaveCount=no",
"Text=test1",
LAST);
web_url("login.pl_2",
"URL=http://127.0.0.1:1080/WebTours/login.pl?intro=true",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/login.pl",
"Snapshot=t11.inf",
"Mode=HTTP",
LAST);
web_concurrent_end(NULL);
web_concurrent_start(NULL);
web_url("flights.gif",
"URL=http://127.0.0.1:1080/WebTours/images/flights.gif",
"Resource=1",
"RecContentType=image/gif",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home",
"Snapshot=t12.inf",
LAST);
web_url("itinerary.gif",
"URL=http://127.0.0.1:1080/WebTours/images/itinerary.gif",
"Resource=1",
"RecContentType=image/gif",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home",
"Snapshot=t13.inf",
LAST);
web_url("in_home.gif",
"URL=http://127.0.0.1:1080/WebTours/images/in_home.gif",
"Resource=1",
"RecContentType=image/gif",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home",
"Snapshot=t14.inf",
LAST);
web_url("signoff.gif",
"URL=http://127.0.0.1:1080/WebTours/images/signoff.gif",
"Resource=1",
"RecContentType=image/gif",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home",
"Snapshot=t15.inf",
LAST);
web_concurrent_end(NULL);
if (atoi(lr_eval_string("{no}"))>=1) {
lr_end_transaction("Login", LR_PASS);
}
else{
lr_end_transaction("Login", LR_FAIL);
}
web_url("SignOff Button",
"URL=http://127.0.0.1:1080/WebTours/welcome.pl?signOff=1",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home",
"Snapshot=t16.inf",
"Mode=HTTP",
LAST);
本章节主要学习了“性能测试基础篇—LoadRunner脚本编写”的内容,大家喜欢的话记得每天来这里和小编一起学习涨薪技能哦。(笔芯)
附:川石信息全国校区最新开班时间,课程资料获取13691729932(微信同号)。