在录制过程中可能会录制到一些非HTML的元素(如XML、JS),主要用于包含去获取自己的一些资源,如JS文件用于调用加载多个图片。对于这类非HTML的元素,录制时有3种方式:
  1)录制时对于非HTML资源并不会生成一个新的功能。它列出所有相关资源的参数,如web_url、web_link和web_submit_data。这些功能的参数使用EXTRARES表示,如下代码:
web_url("WebTours",
"URL=http://127.0.0.1:1080/WebTours/",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTML",
EXTRARES,
"Url=http://act.cmcmcdn.com/upload/201507/8afc2fe48db9060fe1bdda2089e1d950.png", ENDITEM,
"Url=http://act.cmcmcdn.com/upload/201507/3b491068507d8f85ea7b35d756da7215.png", ENDITEM,
LAST);
  2)在一个组中记录这些单独的步骤,为每个非HTML资源创建一个新的功能,如下代码:
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_concurrent_start(NULL);
web_url("8afc2fe48db9060fe1bdda2089e1d950.png",
"URL=http://act.cmcmcdn.com/upload/201507/8afc2fe48db9060fe1bdda2089e1d950.png",
"TargetFrame=",
"Resource=1",
"RecContentType=image/png",
"Referer=http://127.0.0.1:1080/WebTours/",
"Snapshot=t5.inf",
LAST);
web_url("3b491068507d8f85ea7b35d756da7215.png",
"URL=http://act.cmcmcdn.com/upload/201507/3b491068507d8f85ea7b35d756da7215.png",
"TargetFrame=",
"Resource=1",
"RecContentType=image/png",
"Referer=http://127.0.0.1:1080/WebTours/",
"Snapshot=t6.inf",
LAST);
web_concurrent_end(NULL);
  3)不记录,对于非HTML元素不记录,如下代码:
web_url("WebTours",
"URL=http://127.0.0.1:1080/WebTours/",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTML",
LAST);
  URL-based script方式
  将每条客户端发出的请求录制成一条语句,对LR来说,在该模式下,一条语句只能建立一个到服务器的连接,并将通信过程中的很多隐藏信息都录制出来(如session、cookie)。LR提供了web_concurrent_start()和web_concurrent_end()函数模拟URL-based scriptr的工作方式。如下代码:
web_url("WebTours",
"URL=http://127.0.0.1:1080/WebTours/",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTTP",
LAST);
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("www.baidu.com",
"URL=http://www.baidu.com/",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t3.inf",
"Mode=HTTP",
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=t4.inf",
LAST);
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=t5.inf",
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=t6.inf",
"Mode=HTTP",
LAST);
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=t7.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=t8.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=t9.inf",
LAST);
web_concurrent_start(NULL);
web_url("8afc2fe48db9060fe1bdda2089e1d950.png",
"URL=http://act.cmcmcdn.com/upload/201507/8afc2fe48db9060fe1bdda2089e1d950.png",
"Resource=1",
"RecContentType=image/png",
"Referer=http://127.0.0.1:1080/WebTours/",
"Snapshot=t10.inf",
LAST);
web_url("3b491068507d8f85ea7b35d756da7215.png",
"URL=http://act.cmcmcdn.com/upload/201507/3b491068507d8f85ea7b35d756da7215.png",
"Resource=1",
"RecContentType=image/png",
"Referer=http://127.0.0.1:1080/WebTours/",
"Snapshot=t11.inf",
LAST);
web_concurrent_end(NULL);
lr_think_time(12);
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=t12.inf",
"Mode=HTTP",
ITEMDATA,
"Name=userSession", "Value=120572.172620494zcAVQDVpVcQVzzzHDHcVVpfizHHf", ENDITEM,
"Name=username", "Value=test1", ENDITEM,
"Name=password", "Value=test1", ENDITEM,
"Name=JSFormSubmit", "Value=off", ENDITEM,
"Name=login.x", "Value=0", ENDITEM,
"Name=login.y", "Value=0", 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=t13.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=t14.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=t15.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=t16.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=t17.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=t18.inf",
LAST);
web_concurrent_end(NULL);
return 0;
  关于URL高级设置有两种方式:
  1)create concurrent groups for resources their source HTML page。将捕获所有HTML页面的资源并保存在并发组中(并发组使用web_concurrent_start和web_concurrent_end satements 两个函数表示,如上面的代码,是使用了这种方式,故不再重复贴代码了),如果不选中该选项,HTML页面资源将会分成独立的、单独的web_url步骤,但不放入并行组中。
  2)use web_custom_request only。如果录制的是非浏览器的应用程序,可以设置VuGen自定义HTTP请求。在LR中使用web_custom_request函数来实现,代码如下:
web_custom_request("WebTours",
"URL=http://127.0.0.1:1080/WebTours/",
"Method=GET",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTTP",
LAST);
  相对比两种录制模式,可参考下面的原则:
  a.基于浏览器的应用程序推荐用HTML;反之,则用URL
  b.如果基于浏览器的应用程序包含了JS,并且该脚本向服务器发送了请求,比如DataGrid的分布按钮,推荐用URL
  c.基于浏览器的应用程序中使用了HTTPS安全协议,建议使用URL。
  如果使用HTML模式录制后不能成功回话,可以考虑改用URL模式来录制,因为这种情况多是同上面所列举的原因所引起的。
  2、advanced选项卡
  是设置脚本回话的高级选项。(如图9所示)


  
图9

  1)save snapshot resources locally:表示运行结果中保存一个快照
  2)add comments to script for HTTP errors while recording:表示出现错误时会自动添加注释。在Heades可以选择需要录制的heades,以便服务器能够正确处理编辑信息。需要注意的是accept-language选项,像websphere这类服务器会根据HTTP请求中的header来确定编码。
  3、correlation选项卡
  用来对脚本中的关联属性进行设置(如图10所示)。LR包括两种规则:内建规则和自定义规则;LR会默认自带一些内建规则。在录制时选中需要的关联规则,录制脚本过程中LR会自动匹配需要关联的规则,并生成关联函数。如果当前这些关联规则无法满足录制的需求,那么可以单击new application按钮来新建一个关联,再单击new rule按钮为该关联新建一个规则。


 
 图10

  run-time settings 设置
  主要用于设置在脚本运行过程中脚本运行的策略,需要注意的设置项有run logic选项卡、pacing选项卡、think time选项卡和miscellaneous选项卡,具体解释可查看我的另一篇博客,都有详细的描述。在这里不再赘述了
  脚本完善
  脚本录制完成并不代表这些脚本很接近用户的真实使用情况,为了使这些脚本更接近用户的真实使用情况,需要对脚本进行完善。
  首先,为了衡量服务器对某个动作处理的响应时间,需要定义事务(transaction)。例如一个登录动作,为了衡量多用户同时执行登录服务器的响应时间,可以将此操作定义为一个事务。其次,为了衡量加重负载的情况下服务器的性能情况,需要插入集合点。例如为了模拟50个用户同时登录的情况,要做到真正的并发,必须添加一个集合点,用来衡量服务器的性能。
  后,在录制脚本的过程中添加注释,以增强脚本的可读性 。
  1、插入事务
  事务即客户要实现的业务,事务响应时间的原理是将业务操作结束时的时间点与业务开始时的时间点的差值,反应了业务的响应时间,也即是事务由两部分组成:开始事务函数和结束事务函数。
  插入事务有两种方法:一种是在录制过程中插入开始和结束事务点;另一种是在编辑脚本时插入。一般情况下是在录制过程中插入。
  录制过程中需要插入事务点时,单击录制工具栏中的‘插入开始事务点’按钮,输入开始事务名称(要遵守脚本编辑命名规则)。事务的开始点与事务的结束点具有一一对应的特点,即在脚本中插入了一个事务开始点后一定要在接下来的过程中插入结束事务点,插入结束事务点的操作与插入开始事务点的操作一致,当该业务流程执行完毕后需要添加结束事务点,在工具栏中点击‘插入结束事务点’即可。
  如完成后才插入的话,在生成的脚本中找到要插入开始事务点的地方,选择insert->start transaction,在弹出的对话框中输入开始事务的名称;找到要插入结束事务点的地方。(注:同样的操作,但在‘结束事务’对话框中,比在脚本录制过程中插入结束事务点时的对话框中多出了一项transaction status,如图11所示)。
  1)事务的状态被自动设置,如果事务执行成功,状态设置为PASS;如果事务执行失败,状态设置为FAIL;如果事务异常中断,状态设置为STOP。
  2)事务执行成功,代码返回的状态是PASS。
  3)事务执行失败,代码返回的是FAIL。
  4)事务异常中断,代码返回的状态是STOP。
  为什么需要事务状态?因为在测试过程中不单需要获取事务的响应时间,还需要确定事务是否成功,如果响应时间很短,但事务都失败了,那也是无法满足客户需求的。


  
图11

  回放脚本后,事务结束状态为PASS并不一定代表业务一定做成功了(比如将登录脚本的账户密码改为错误的,可看下面贴的代码),因为LR在自动判断事务结束状态时是以结束函数是否运行为标准,即只要结束事务函数运行,那么将事务的结束状态置为PASS,反之将事务的结束状态置为FAIL。正常情况应该是登录后,先检查登录账户名是否正确,如果检查到正确后才将事务的结束状态置为PASS,这样才能保证业务成功了,否则做性能测试根本毫无意义。所以一般情况下都需要先设置检查点后,再根据检查点来判断事务是否成功,这样才比较合理。
web_url("WebTours",
"URL=http://127.0.0.1:1080/WebTours/",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTML",
EXTRARES,
"Url=http://act.cmcmcdn.com/upload/201507/8afc2fe48db9060fe1bdda2089e1d950.png", ENDITEM,
"Url=http://act.cmcmcdn.com/upload/201507/3b491068507d8f85ea7b35d756da7215.png", ENDITEM,
LAST);
lr_start_transaction("登录");
lr_think_time(16);
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=t3.inf",
"Mode=HTML",
ITEMDATA,
"Name=userSession", "Value=120579.391332523zcAcDAcpttVzzzzHDHcVDpiVtzf", ENDITEM,
"Name=username", "Value=test1", ENDITEM,
"Name=password", "Value=test1", ENDITEM,
"Name=JSFormSubmit", "Value=off", ENDITEM,
"Name=login.x", "Value=45", ENDITEM,
"Name=login.y", "Value=12", ENDITEM,
LAST);
lr_end_transaction("登录",LR_AUTO);
return 0;
  注:回放这个脚本时,事务的结束状态为PASS,这是正确的,因为这个脚本是可以正确的登录的。
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=t3.inf",
"Mode=HTML",
ITEMDATA,
"Name=userSession", "Value=120579.391332523zcAcDAcpttVzzzzHDHcVDpiVtzf", ENDITEM,
"Name=username", "Value=error1", ENDITEM,
"Name=password", "Value=test1", ENDITEM,
"Name=JSFormSubmit", "Value=off", ENDITEM,
"Name=login.x", "Value=45", ENDITEM,
"Name=login.y", "Value=12", ENDITEM,
LAST);
  注:下面是回放日志,可以看到虽然是错误的账号,但仍被置为PASS状态,这是不正确的。
Action.c(4): web_url("WebTours") was successful, 9467 body bytes, 2222 header bytes      [MsgId: MMSG-26386]
Action.c(16): Notify: Transaction "登录" started.
Action.c(20): web_submit_data("login.pl") was successful, 795 body bytes, 225 header bytes      [MsgId: MMSG-26386]
Action.c(36): Notify: Transaction "登录" ended with "Pass" status (Duration: 0.3466 Wasted Time: 0.0031).
Ending action Action.
  2、插入集合点
  集合点是指在脚本中插入集合点函数(lr_rendezvous),当脚本运行到集合点函数时,将停止运行并等待其允许运行的条件(其允许运行条件即为集合点策略)达到后才释放集合点开始运行。也即在性能测试过程中如果需要保证虚拟用户真正并发,即必须添加集合点。
  集合点可以在录制过程中插入,也可以在录制完成后插入,但只能插入action部分的脚本中,不能插入vuser_int和vuser_end两部分脚本中。
  录制过程中需要插入集合点的位置时,单击录制工具栏中的‘插入集合点’按钮即可插入集合点;如完成后才插入的话,可选择insert->rendezvous,在弹出的‘插入集合点’对话框中输入要集合点名称。(注意命名时要遵守脚本编辑命名规则,使其具有代表性)。
  3、插入注释
  注释可以在录制脚本过程中插入,也可以在脚本录制完成后插入。
  录制过程中需要对录制的脚本进行注释时,单击录制工具栏中的‘插入注释’按钮即可插入注释;如完成后才插入的话,可选择insert->comment,在弹出的‘插入注释’对话框中输入要注释的内容。