一、前言:
本文适合初学者,包含很多细节,也作为自己的备忘。有不准确之处,欢迎指正。
二、准备:
1、以虚拟机中的Linux系统作为服务器,开启bugfree服务。
2、以fiddler作为抓包工具,辅助脚本开发。
3、脚本流程:bugfree登录--创建bug--解决bug。
三、实现过程:
3.1 脚本主体
创建空白脚本后,结构如下图,压力测试中一般我们将登录单独放在vuser_init中。这里为了整个流程更清晰,脚本全部写在Action中。
值得注意的是,我们设置脚本迭代次数,只对Action部分生效,而vuser_init和vuser_end只会运行一次。
开始写脚本前,我们对fiddler先进行一些过滤设置。
在浏览器进行bugfree登录操作,fiddler抓取到结果,比较有用的信息是URL以及Headers项的请求类型(这里是POST)、参数列表。
如果参数中以及后文的检查点文本中含有中文,需要设置loadrunner支持UTF-8编码格式,将含有UTF-8的项改为“Yes”:
在已知请求类型为POST的前提下,执行Insert--new step--Submit Data。如果是GET请求,则需要插入Url函数。
写入General信息:
写入参数信息:
点击确定后生成脚本。创建bug和解决bug与此类似,生成脚本如下:
web_submit_data("创建缺陷", "Action=http://192.168.232.132/bugfree/info/edit?type=bug&action=opened&product_id=1", "Method=POST", "TargetFrame=", "Referer=", "Mode=HTTP", ITEMDATA, "Name=BugInfoView[deleted_file_id]", "Value=", ENDITEM, "Name=BugInfoView[lock_version]", "Value=", ENDITEM, "Name=BugInfoView[product_id]", "Value=1", ENDITEM, "Name=isPageDirty", "Value=1", ENDITEM, "Name=templateTitle", "Value=", ENDITEM, "Name=BugInfoView[title]", "Value=mutest20170927{title}", ENDITEM, "Name=layer1_module", "Value=0", ENDITEM, "Name=BugInfoView[productmodule_id]", "Value=0", ENDITEM, "Name=BugInfoView[assign_to_name]", "Value=系统管理员", ENDITEM, "Name=BugInfoView[mail_to]", "Value=", ENDITEM, "Name=BugInfoView[severity]", "Value=1", ENDITEM, "Name=BugInfoView[priority]", "Value=1", ENDITEM, "Name=Custom[BugType]", "Value=代码错误", ENDITEM, "Name=Custom[HowFound]", "Value=代码检查", ENDITEM, "Name=Custom[BugOS]", "Value=Linux", ENDITEM, "Name=Custom[BugBrowser]", "Value=Chrome", ENDITEM, "Name=Custom[OpenedBuild]", "Value=Mu", ENDITEM, "Name=Custom[ResolvedBuild]", "Value=", ENDITEM, "Name=Custom[BugSubStatus]", "Value=", ENDITEM, "Name=Custom[BugMachine]", "Value=", ENDITEM, "Name=Custom[BugKeyword]", "Value=", ENDITEM, "Name=BugInfoView[related_bug]", "Value=", ENDITEM, "Name=BugInfoView[related_case]", "Value=", ENDITEM, "Name=BugInfoView[related_result]", "Value=", ENDITEM, "Name=attachment_file[]", "Value=", ENDITEM, "Name=BugInfoView[action_note]", "Value=", ENDITEM, "Name=BugInfoView[repeat_step]", "Value=<br />", ENDITEM, LAST); web_submit_data("解决缺陷", "Action=http://192.168.232.132/bugfree/bug/1/resolved", "Method=POST", "TargetFrame=", "Referer=", "Mode=HTTP", ITEMDATA, "Name=BugInfoView[deleted_file_id]", "Value=", ENDITEM, "Name=BugInfoView[lock_version]", "Value=1", ENDITEM, "Name=BugInfoView[product_id]", "Value=1", ENDITEM, "Name=isPageDirty", "Value=1", ENDITEM, "Name=templateTitle", "Value=", ENDITEM, "Name=BugInfoView[title]", "Value=mutest20170927{title}", ENDITEM, "Name=layer1_module", "Value=0", ENDITEM, "Name=BugInfoView[productmodule_id]", "Value=0", ENDITEM, "Name=BugInfoView[assign_to_name]", "Value=系统管理员", ENDITEM, "Name=BugInfoView[mail_to]", "Value=", ENDITEM, "Name=BugInfoView[severity]", "Value=1", ENDITEM, "Name=BugInfoView[priority]", "Value=1", ENDITEM, "Name=Custom[BugType]", "Value=代码错误", ENDITEM, "Name=Custom[HowFound]", "Value=代码检查", ENDITEM, "Name=Custom[BugOS]", "Value=全部", ENDITEM, "Name=Custom[BugBrowser]", "Value=Chrome", ENDITEM, "Name=Custom[OpenedBuild]", "Value=Mu", ENDITEM, "Name=Custom[ResolvedBuild]", "Value=Mu", ENDITEM, "Name=BugInfoView[solution]", "Value=By Design", ENDITEM, "Name=BugInfoView[duplicate_id]", "Value=", ENDITEM, "Name=Custom[BugSubStatus]", "Value=Hold", ENDITEM, "Name=Custom[BugMachine]", "Value=", ENDITEM, "Name=Custom[BugKeyword]", "Value=", ENDITEM, "Name=BugInfoView[related_bug]", "Value=", ENDITEM, "Name=BugInfoView[related_case]", "Value=", ENDITEM, "Name=BugInfoView[related_result]", "Value=", ENDITEM, "Name=attachment_file[]", "Value=", ENDITEM, "Name=BugInfoView[action_note]", "Value=", ENDITEM, "Name=BugInfoView[repeat_step]", "Value=<br />", ENDITEM, LAST);
至此,脚本主体完成。但这样的脚本很显然是不符合压测要求的:
1、如果对登录单独压测,因为登录是查询型请求,与修改型请求不同,我们在数据库中无从校验其成功率。所以设置登录检查点函数是必要的。
2、如果登录用户名不允许多IP同时登录,那么对用户名进行参数化是必要的。参数化的本质是应对数据库对参数值进行的唯一性校验。
3、观察脚本,解决Bug步骤,其Bug ID是固定的,那么执行压测时,我们就一直在对同一个Bug进行解决缺陷操作,这显然是不合理的。我们需要将Bug ID和上一步创建Bug的Bug ID关联起来。这样,才能保证每次解决缺陷都是针对上一步刚刚创建的处于未解决状态的Bug。
接下来,对脚本进行改进:
3.2 参数化
参数化设置的目的是应对数据库对参数值的唯一性校验。其设置方式如下:
3.1.1 参数化设置
打开Parameter List,点击左下角的【New】,创建新的参数。
生成之后,右键选中待替换内容,选择【Use Existing Parameter】:
或者,也可以直接选中待参数化内容,右键后选择【Replace with a Parameter】,一次性进行创建参数和替换。
3.2.2 Mysql数据库数据获取
设置参数化之后,面临的问题就是大量数据从何而来。利用存储过程在数据库中创建大量数据,然后参数化文件读取这些数据,是个不错的方法。
Next
Next
Next
Next
Next
Next
Next,写入sql语句
数据已导入:
参数类型即Parameter type还可选择其他类型,常用的如Date/Time,Rondom Nunmber(可用于注册型请求的用户名参数)等,遇到实际场景后补充。
3.3 检查点设置
以登录为例,只要我们提交了请求,即使不能成功登录,服务器返回给我们的状态码依然是200,而loadrunner判断请求是否成功的依据就是返回的状态码,所以在未加检查点之前,我们无从判断登录业务是否成功。当然,非查询类请求,可以去数据库进行校验。
因此我们需要加入检查点来进行业务是否真正成功的校验。检查点的思想是,在请求响应内容中,查找特殊字段,例如登录请求响应中的“欢迎”、“退出”等等字段。下面以Bug登录为例,进行检查点详细解析。
首先,我们去分析登录请求中,检查点需要设置的文本内容。
HTTP/1.1 302 Found Date: Sun, 10 Dec 2017 10:07:38 GMT Server: Apache/2.4.7 (Unix) OpenSSL/1.0.1f PHP/5.5.9 mod_perl/2.0.8-dev Perl/v5.16.3 X-Powered-By: PHP/5.5.9 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Set-Cookie: PHPSESSID=a3dta30c68sb9l7fc9t4ancp03; path=/ Set-Cookie: language=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/ Set-Cookie: language=bece46be16477e1ab82f9d40a53074cb0a54e105s%3A5%3A%22zh_cn%22%3B; expires=Tue, 09-Jan-2018 10:07:39 GMT; Max-Age=2592000; path=/ Location: http://192.168.232.128/bugfree/index.php Content-Length: 0 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html
在响应内容中,貌似没有特殊字段。只有“302”值得注意,这个状态码表示请求发生重定向,对象已临时移动。那么,也只有登录成功之后,才能进行跳转首页的重定向。为了验证该结论,我做一次失败登录。第二次失败登录与第一次成功登录相比,缺少了后续跳转首页等请求。
再观察其响应内容,状态码是200。至此,我们可以选定登录检查点函数的检查文本为“302”。
HTTP/1.1 200 OK Date: Sun, 10 Dec 2017 11:07:03 GMT Server: Apache/2.4.7 (Unix) OpenSSL/1.0.1f PHP/5.5.9 mod_perl/2.0.8-dev Perl/v5.16.3 X-Powered-By: PHP/5.5.9 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Content-Length: 4039 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html
执行Insert--new step--web_reg_find,reg表示注册型函数,这类函数作用于其下文第一个函数。
生成脚本如下:
web_reg_find("Fail=NotFound", "Search=All", "SaveCount=count", "Text=302", LAST); web_submit_data("login", "Action=http://192.168.232.128/bugfree/index.php/site/login", "Method=POST", "TargetFrame=", "Referer=", "Mode=HTTP", ITEMDATA, "Name=LoginForm[username]", "Value=admin", ENDITEM, "Name=LoginForm[password]", "Value=123456", ENDITEM, "Name=LoginForm[language]", "Value=zh_cn", ENDITEM, "Name=LoginForm[rememberMe]", "Value=0", ENDITEM, LAST);
运行脚本:
Action.c(10): Registered web_reg_find successful for "Text=302" (count=2) [MsgId: MMSG-26364] Action.c(10): web_submit_data("login") was successful, 76514 body bytes, 1898 header bytes, 14 chunking overhead bytes [MsgId: MMSG-26385]
回到脚本中,修改密码为“1234567”,运行脚本进行反向验证:
Action.c(10): Error -26366: "Text=302" not found for web_reg_find [MsgId: MERR-26366] Action.c(10): web_submit_data("login") highest severity level was "ERROR", 4041 body bytes, 465 header bytes [MsgId: MMSG-26388]
至此,检查点设置成功。检查点特殊文本内容的确定需要一点点自己的分析。
3.3 关联设置
一、为什么要设置关联,常见场景有以下两个:
1、登录请求中,含有formhash、sessionid等特殊参数,这些参数的值每次请求将发生更新,而脚本中如果将其固定,下次请求仍然提交上次使用的参数值,请求将会失败。
2、创建缺陷生成Bug ID,解决缺陷请求依赖该Bug ID,请求参数中Bug ID的值需要随着创建缺陷生成的Bug ID同步更新。
下面以Bugfree解决缺陷为例,讲述如何设置关联:
首先,在【创建缺陷】请求的响应中查找“57730”,该ID是我新创建的。
截取其中一段包含“57730”的字段:
location=‘/bugfree/index.php/bug/57730‘;</
执行Insert--new step--web_reg_save_param,其中BugID是参数名,具备一定意义即可,后续请求需要引用该参数名。
生成脚本如下:
web_reg_save_param("BugID", "LB=location=‘/bugfree/index.php/bug/", "RB=‘;</", "Search=All", LAST);
为了查看关联参数捕捉情况,开启扩展日志:
运行脚本,观察Replay Log,关联参数捕捉成功:
Action.c(23): Registering web_reg_save_param was successful [MsgId: MMSG-26390] Action.c(29): Notify: Saving Parameter "BugID = 57732".
关联参数捕捉结束,接下来很简单,传递捕获的参数到后续请求中,将具体值替换成引用值即可:
再次运行脚本:
Action.c(23): Registering web_reg_save_param was successful [MsgId: MMSG-26390] Action.c(29): Notify: Saving Parameter "BugID = 57734". Action.c(29): web_submit_data("创建缺陷") was successful, 28553 body bytes, 412 header bytes, 25 chunking overhead bytes [MsgId: MMSG-26385] Action.c(65): Notify: Parameter Substitution: parameter "BugID" = "57734" Action.c(65): web_submit_data("解决缺陷") was successful, 30589 body bytes, 412 header bytes, 25 chunking overhead bytes [MsgId: MMSG-26385]
关联基本应用到此为止,后续补充复杂的关联应用,例如关联数组。
3.4 脚本调试
脚本调试应遵循分步调试原则,由上至下。比如,先将【创建缺陷】、【解决缺陷】注释,单独放开【登录】,对其进行调试。通过View--Test result可查看Html格式视图,便于排查脚本中的错误。例如下图,表明UTF-8未设置,导致中文参数值不被识别: