LoadRunner性能测试系统学习教程:Vuser发生器(上)
引言
Vuser发生器(VisualUserGenerator,简称为VuGen)主要通过捕获客户端向服务器发送的HTTP请求,将这些请求录制成脚本,在回放时将捕获的HTTP请求再次发送,以达到模拟客户的行为的目的,所以Vuser主要是用来捕获最终用户业务流程和创建自动化测试脚本,即生成测试脚本。VuGen是录制测试脚本、编辑与完善测试脚本的一个平台,支持C语言语法。
主要包括以下内容:
脚本录制
Recording Options设置
Run-Time Settings设置
脚本完善
脚本录制
启动VisualUserGenerator,创建一个新的脚本,开始录制脚本,在录制脚本过程中,VuGen会自动捕获操作过程中客户端与服务器端进行通信的所有数据。这里涉及的关键点是如何选择录制协议。
脚本开发主要包括四大步骤:计划、录制脚本、脚本增强和单机调试脚本,如图所示。
如何选择协议
在创建一个新的脚本时,首先会弹出一个对话框,在该对话框中选择录制时需要的协议,这步非常重要,选择的协议将直接影响到录制后的脚本是否理想,如何选择录制协议是录制前必须要解决的问题。
各种协议和相关头文件的对应关系如图所示。
协议 |
头文件 |
AJAX(Click&Script) |
web_ajax.h |
Citrix |
ctrxfuncs.h |
COM/DCOM |
lrc.h |
Database |
lrd.h |
FTP |
mic_ftp.h |
General C function |
lrun.h |
IMAP |
mic_imap.h |
LDAP |
mic_midap.h |
MAPI |
mic_mapi.h |
Oracle NCA |
orafuncs.h |
POP3 |
mic_pop3.h |
ROP |
lrrdp.h |
SAPGUI |
as_sapgui.h |
SAP(Click&Script) |
sap_api.h |
Siebel |
lrdsiebel.h |
SMTP |
mic_smtp.h |
Terminal Emulator |
lrrte.h |
WAP |
as_wap.h |
Web(HTML\HTTP) |
as_wab.h |
Web(Click&Script) |
web_api.h |
Web Services |
wssoap.h |
Windows Sockets |
lrs.h |
选择协议的常用方法主要有以下几种:
1.最简单的方法就是向开发工程师确认数据通信所采用的协议,因为开发工程师最清楚应用程序采用的是何种通信协议。
2.没有开发工程师支持时,可以通过概要或详细设计手册获知所使用的协议。
3.使用协议分析工具捕获通信时的数据包并进行分析,然后确定被测对象所使用的协议。在使用协议分析工具分析协议过程中一定要摒除底层协议,不要被底层协议所迷惑。
4.根据以往测试经验来判断被测试对象采用的协议,这种方法具有猜测性,有时候不一定准确。
在LoadRunner新的版本中有一个协议分析的工具(ProtocolAdvisor),通过该工具可以分析当前系统所使用的协议。协议分析工具的使用步骤如下:
1.在【WelcometotheVirtualUserGenerator】界面单击【ProtocolAdvisor】按钮,如图所示。
2.弹出【ProtocolAdvisor】对话框,如图所示。
Applicationtype:选择应用程序的类型,被测试的应用程序类型主要包括两类:InternetApplictions和Win32Applications,也即通常说的B/S和C/S框架的两类应用程序。
Programtoanalyze:分析的程序,如果选择的应用程序类型为InternetApplications那么,该选项为MicrosoftInternetExplorer,即IE浏览器,因为LoadRunner在录制时默认启动IE浏览器进行录制;如果选择的应用程序类型为Win32Applications,那么该选项为需要测试的应用程序的路径。
URLAddress:即需要分析的网页地址;
Workingdirectory:工作目录,默认的为LoadRunner所在路径的bin目录。
3.设置待分析程序的路径或URL地址,单击【OK】按钮,即开始分析应用程序,通常分析一个简单的业务即可停止分析。
4.单击浮动框中的【StopAnalyzing】按钮,停止分析应用程序,并产生分析后的结果。
LoadRunner提供了多种协议,具体的协议分类见表。
1.单协议脚本:创建单协议Vuser脚本,在对话框中选择录制时需要的协议,如图所示。创建新脚本时,会弹出一个对话框,LoadRunner提供三种选择协议的方式:单协议脚本、多协议脚本和最近使用过的协议。
2.多协议脚本:创建多协议Vuser脚本。在AvailableProtocols中选择一个或多个协议,点击右箭头,将其移入到SelectedProtocols部分中,同样,在SelectedProtocols中选择一个或多个协议,点击左箭头可以移除选中的协议,如图所示。
3.最近使用过的协议:从最近录制脚本的协议列表中,选择一种协议进行录制,如图所示
开始录制脚本
协议选择好后可以开始录制脚本。这里以Web(HTTP/HTML)协议为例进行录制。
VuGen录制浏览器主要是通过代理的方式来实现的。开始录制时,VuGen打开浏览器(默认使用Mircosoft自带的IE浏览器,使用其它浏览器录制容易出现HTTP请求被丢失的现象,所以尽量使用IE浏览器进行录制),并以VuGen作为代理来访问目标服务器。这样,VuGen就可以捕获客户端与服务器之间通过的数据包,如图所示。
在使用VuGen进行录制用户操作时,VuGen会对捕获的数据进行分析,并将其还原成对应协议的由API组成的脚本。同时,VuGen会将这些函数生成的脚本插入到VuGen编辑器中,以创建原始的Vuser脚本。
录制时系统弹出一个录制窗口,如图所示。
在URLAddress中输入要录制的站点地址。RecordintoAction选项表示将录制的代码放到哪个部分。LoadRunner生成的代码由三部分组成:vuser_init、Action和vuser¬_end。
注意:一般情况下都是将生成的代码放在Action部分,因为vuser_init和vuser¬_end两部分的代码只会执行一次,这样会出现这种问题,客户的并发虚拟用户只执行一次,执行完成一次后再也不执行,这样就没有HTTP请求给服务器,也即服务器没有压力。如下例子,图是每秒点击率的值。
从图中可以看出,在场景执行到25秒后,客户端的点击率为0,即25秒后客户端没有提交任何请求,这就是典型的由于将脚本放在vuser_init引起的,因为脚本放在vuser_init中,导致每个虚拟用户只会执行一次这部分的脚本,当用户加载完成后,再也不行,所以看到后期的点击率都为0。
Recordtheapplicationstartup选项表示应用程序一旦启动,VuGen就立即开始录制;如果不选中,应用程序启动后,VuGen会弹出如图所示的对话框,并且暂时不会进行录制,当用户操作应用程序到需要录制的地方时,点击Record按钮,VuGen才开始录制。默认情况下Recordtheapplicationstartup是选中的状态。点击Record按钮开启录制。
在录制前还需要注意在RecordinOptions设置对话框中,设计脚本录制的方式,关于脚本的录制方式将在3.2.1小节中详细介绍。
开始录制后,会出现如图所示的工具栏
该工具条从左到右依次代表开始录制、暂停录制、停止录制、新建Action、在脚本与录制界面之间切换、添加开始事务标识、添加结束事务标识、设置集合点和添加注释。
录制过程中,LoadRunner会自动记录用户的操作。录制完成后,点击“停止录制”按钮结束录制,这时VuGen会自动生成一个脚本,如图所示。
这是一个比较简单的脚本,但可以看出LoadRunner生成的脚本都是由函数组成。
Recording Options设置
在进行录制时,首先要对录制的一些参数进行设置,只有将这些参数设置好,才能录制并生成需要的脚本。
首先是RecordingOptions设置,需要注意的设置项有:Recording选项卡、Advanced选项卡和Correlation选项卡。
在Tools菜单中选择RecordingOptions或直接按快捷键Ctrl+F7进入参数设置对话框,如图所示。
Recording选项卡
在RecordingOptions对话框中,选择Recording选项卡。RecordingLevel包含两种录制模式:HTML-basedscript和URL-basedscript,如图所示,默认情况下选中HTML-basedscript录制方式。当然,两种录制模式也存在差别
单击【HTMLAdvanced…】按钮,弹出【AdvancedHTML】对话框,如图所示,在该对话框中关于HTML-basedscript脚本方式又有两种:“Ascriptdescribinguseractions”和“Ascriptcontainingexplicit”。
Ascriptdescribinguseractions:模拟用户行为录制,即GUI录制,把用户每一步的操作显示出来,最后生成的脚本非常直观并且会将上下文的一些敏感信息记录下来。它创建URL(web_url)、link(web_link)、image(web_image)和提交表单(web_submit_form)。
下面以Ascriptdescribinguseractions方式录制一个登录的功能,录制后的代码如下:
web_url("WebTours",
"URL=http://127.0.0.1:1080/WebTours/",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTML",
LAST);
lr_think_time(4);
web_submit_form("login.pl",
"Snapshot=t2.inf",
ITEMDATA,
"Name=username","Value=test1",ENDITEM,
"Name=password","Value=1",ENDITEM,
"Name=login.x","Value=65",ENDITEM,
"Name=login.y","Value=8",ENDITEM,
LAST);
return0;
从生成的代码中可以看到,在录制时只做了两个操作,生成的代码也只有两个函数,也即这种录制模式只录制用户的操作,其它的内容不会被录制。使用的提交信息函数为web_submit_form()。
AscriptcontaningexplictURLsonly:录制所有links(链接)、images(图片)和URL(web_url),但不创建web_link、web_image和提交表单(web_submit_form)。这种录制方式生成的脚本不直观。
下面以AscriptcontaningexplictURLSonly方式录制一个登录的功能,录制后的代码如下:
web_url("WebTours",
"URL=http://127.0.0.1:1080/WebTours/",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTML",
LAST);
web_submit_data("login.pl",
"Action=http://127.0.0.1:1080/WebTours/login.pl",
"Method=POST",
"TargetFrame=body",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home",
"Snapshot=t2.inf",
"Mode=HTML",
ITEMDATA,
"Name=userSession","Value=108748.859052248ffQDHzQptDHfDDfzcpzVzzcf",ENDITEM,
"Name=username","Value=test1",ENDITEM,
"Name=password","Value=1",ENDITEM,
"Name=JSFormSubmit","Value=off",ENDITEM,
"Name=login.x","Value=53",ENDITEM,
"Name=login.y","Value=10",ENDITEM,
LAST);
return0;
从生成的代码中可以看到,同样的提交登录的信息但使用的函数为web_submit_data(),不再以表单的方式提交,而web_submit_form()函数则是以表单的信息进行提交的,该函数运行时,首先在页面上去查找表单,再提交数据,而web_submit_data()则不需要,直接向服务器发送要提交的数据。
但在录制过程中很可能会录制到一些非HTML的元素(如Java小程序、XML、ActiveX元素、JavaScript),这些非HTML元素主要用于包含或去获取自己的一些资源,例如,JavaScript的JS文件用于调用加载多个图片。对于这类非HTML的元素,录制时有三种方式:
Recordwithinthecurrentscriptstep:在录制时对于非HTML资源并不会生成一个新的功能。它列出所有相关资源的参数,如web_url、web_link和web_submit_data。这些web功能的参数使用EXTRARES标示。
如以下代码:
web_url("index.asp",
"URL=http://www.daisy.com/index.asp",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t2.inf",
"Mode=HTML",
EXTRARES,
"Url=http://www.daisy.com/ScrollApplet.class","Referer=",ENDITEM,
"Url=http://www.daisy.com/board.txt","Referer=",ENDITEM,
"Url=http://www.daisy.com/nav_login1.gif",ENDITEM,
...
LAST);
Recordinseparatestepsanduseconcurrentgroups:在一个组中记录这些单独的步骤,为每个非HTML资源创建一个新的功能(但不包括一些页面的功能,如web_url、web_link等)。所有的web_url资源都将放置并行组中(并行组由web_concurrent_start和web_concurrent_end进行标示)。
如以下代码:
web_url("index.asp",
"URL=http://www.daisy.com/index.asp",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t2.inf",
"Mode=HTML",
LAST);
web_concurrent_start(NULL);
web_url("ScrollApplet.class",
"URL=http://www.daisy.com/ScrollApplet.class",
"Resource=1",
"RecContentType=application/octet-stream",
"Referer=",
LAST);
web_url("board.txt",
"URL=http://www.daisy.com/board.txt",
"Resource=1",
"RecContentType=text/plain",
"Referer=",
LAST);
web_concurrent_end(NULL);
Donotrecord:不记录,对于非HTML元素不记录。
注意:使用HTML-Based模式录制时,VuGen插入目标帧到web_url函数中时,VuGen会在run-time运行的浏览器中和结果报告中显示页面的正确性。
如以下代码:
web_url("buttonhelp.gif",
"URL=http://www.hplab.com/home?com/rstr?BV_EngineID...,
"TargetFrame=main",
"Resource=0",
"RecContentType=text/html",
"Referer=http://www.hplab.com/home?...
"Snapshot=t5.inf",
"Mode=HTML",
LAST);
URL-basedscript方式:将每条客户端发出的请求录制成一条语句,对LoadRunner来说,在该模式下,一条语句只能建立一个到服务器的连接,并将通信过程中的很多隐藏的信息都录制出来(如session、cookie)。LoadRunner提供了web_concurrent_start()和web_concurrent_end()函数模拟URL-basedscript的工作方式。
下面以URL-basedscript方式录制一个登录的功能,录制后的代码如下:
web_url("WebTours",
"URL=http://127.0.0.1:1080/WebTours/",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTTP",
LAST);
web_concurrent_start(NULL);
web_url("header.html",
"URL=http://127.0.0.1:1080/WebTours/header.html",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/",
"Snapshot=t2.inf",
"Mode=HTTP",
LAST);
web_url("welcome.pl",
"URL=http://127.0.0.1:1080/WebTours/welcome.pl?signOff=true",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/",
"Snapshot=t4.inf",
"Mode=HTTP",
LAST);
web_concurrent_end(NULL);
web_concurrent_start(NULL);
web_url("hp_logo.png",
"URL=http://127.0.0.1:1080/WebTours/images/hp_logo.png",
"Resource=1",
"RecContentType=image/png",
"Referer=http://127.0.0.1:1080/WebTours/header.html",
"Snapshot=t3.inf",
LAST);
web_url("webtours.png",
"URL=http://127.0.0.1:1080/WebTours/images/webtours.png",
"Resource=1",
"RecContentType=image/png",
"Referer=http://127.0.0.1:1080/WebTours/header.html",
"Snapshot=t5.inf",
LAST);
web_concurrent_end(NULL);
web_concurrent_start(NULL);
web_url("home.html",
"URL=http://127.0.0.1:1080/WebTours/home.html",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/welcome.pl?signOff=true",
"Snapshot=t6.inf",
"Mode=HTTP",
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);
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=108749.254191981ffQDHiQpHzcfDDfzcpHDifHf",ENDITEM,
"Name=username","Value=test1",ENDITEM,
"Name=password","Value=1",ENDITEM,
"Name=JSFormSubmit","Value=off",ENDITEM,
"Name=login.x","Value=63",ENDITEM,
"Name=login.y","Value=9",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=t12.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=t11.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("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=t14.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=t15.inf",
LAST);
web_concurrent_end(NULL);
return0;
选择URL-basedscript选择,单击【URLAdvanced…】,弹出【AdvancedURL】对话框。关于URL的高级设置有两种方式:“CreateconcurrentgroupsforresourcestheirsourceHTMLpage”和“Useweb_custom_requestonly”。
CreateconcurrentgroupsforresourcestheirsourceHTMLpage:将捕获所有HTML页面的资源,并将其保存在并发组中(并发组使用web_concurrent_start和web_concurrent_endstatements两个函数标识),如果不选中该选项时,HTML页面资源将会分成独立的、单独的web_url步骤,但并不放入并行组中。
如以下代码:
web_concurrent_start(NULL);
...
web_url("ClickHereForAdditionalRestrictions",
"URL=http://www.hplab.com/restrictions.html",
"Resource=0",
"RecContentType=text/html",
"Referer=http://www.hplab.com/home?...
"Snapshot=t4.inf",
"Mode=HTTP",
LAST);
web_url("buttonhelp.gif",
"URL=http://www.hplab.com/home?com/rstr?BV_EngineID...,
"Resource=0",
"RecContentType=text/html",
"Referer=http://www.hplab.com/home?...
"Snapshot=t5.inf",
"Mode=HTTP",
LAST);
...
web_concurrent_end(NULL);
Useweb_custom_requestonly:如果录制的是非浏览器的应用程序,可以设置VuGen自定义HTTP请求,在LoadRunner中使用web_custom_reques函数来实现,不管内容如何。
如以下代码:
web_custom_request("www.hplab.com",
"URL=http://www.hplab.com/",
"Method=GET",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTTP",
LAST);
选择HTML-basedscript还是URL-basedscript,应该根据实际需要进行,下面是一些常见的参考原则:
1.基于浏览器的应用程序推荐使用HTML-basedscript。
2.不是基于浏览器的应用程序推荐使用URL-basedscript。
3.如果基于浏览器的应用程序中包含了JavaScript,并且该脚本向服务器发送了请求,比如DataGrid的分页按钮等,推荐使用URL-basedscript。
4.基于浏览器的应用程序中使用了HTTPS安全协议,建议使用URL-basedscript。如果使用HTML-basedscript模式录制后不能成功回放,可以考虑改用URL-basedscript模式来录制。因为这种情况多是由上面所列举的原因所引起的。
Advanced选项卡
Advanced选项是设置脚本回放的高级选项,如图所示。
Savesnapshotresourceslocally:表示运行结果中保存一个快照。
AddcommentstoscriptforHTTPerrorswhilerecording表示出现错误时会自动添加注释。
点击Headers…按钮,会弹出HTTPHeaders配置对话框,如图所示。在该对话框中可以选择需要录制的Headers,以便服务器能够正确处理编辑信息。需要注意的是Accept-Language选项,像Websphere这类服务器会根据HTTP请求中的Header来确定编码。
Correlation选项卡
Correlation选项卡用来对脚本中的关联属性进行设置,如图3-19所示。LoadRunner包括两种规则:一是内建规则;二是:自定义的规则;LoadRunner会默认自带一些内建规格。在录制时选中需要的关联规则,录制脚本过程中LoadRunner会自动匹配需要关联的规格,并生成关联函数。如果当前的这些关联规则无法满足录制的需求,那么可以点击【NewApplication】按钮来新建一个关联,再点击【NewRule】按钮为该关联新建一个规则。