Loadrunner定义了很多用于性能测试脚本编写和增强的方法,本文总结了web测试时经常会使用到的方法,掌握这些方法之后,基本可以不依赖录制就能完成脚本编写和增强。
模拟用户请求,实现HTTP请求中的GET方法。
语法如下:
int web_url( const char *StepName, const char *url, <List of Attributes>, [EXTRARES, <List of Resource Attributes>,] LAST );
//参数说明:
//StepName:测试结果中显示的名称,也可用作自动事务的名称
//url:需要访问的网页地址
//List of Attributes:支持属性如下:
//FtpAscii:FTP下载文件时使用,不常用
//TargetFrame:包含当前资源或连接的frame标签名
//Resource:请求的URL是否是一个资源,0不是,1是
//RecContentType:响应头的文本类型
//Referer:URL引用的页面
//Snapshot:快照名称,用于关联
//Mode:录制模式,HTML或HTTP
//EXTRARES:分界函数,指示下一个参数将是资源属性列表
//List of Resource Attributes:资源属性列表,支持以下资源属性:
//URL:通过URL地址要下载的web资源
//Refer:发送下载请求的页面地址
//ENDITEM:列表中每个资源的标志符
//LAST:参数列表结束标记
返回值如下:
成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)
示例如下:
int status;
status = web_url("index",
"URL=http://127.0.0.1:1080/WebTours/index.htm",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTML",
LAST);
lr_output_message("status=%d",status);
//运行结果:status=0
模拟单击图片,必须在有前置操作的上下文中使用
语法:
int web_image (const char *StepName, <List of Attributes>, [EXTRARES,
<List of Resource Attributes>,] LAST );
返回值:
成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)
示例:
web_image("SignOff Button",
"Alt=SignOff Button",
"Snapshot=t110.inf",
LAST);
//Alt为html结构中图片的alt属性值
模拟用户单击指定属性链接,必须在有前置操作的上下文中使用
语法:
int web_link (const char *StepName, <List of Attributes>, [EXTRARES, <List
of Resource Attributes>,] LAST );
//List of Attributes:支持下列的属性:
//Text:超链接中的文字,必须精确匹配。
//Frame:录制操作时所在的 Frame 的名称。
//TargetFrame、ResourceByteLimit
//Ordinal:如果用给定的Attributes筛选出的元素不唯一,则使用此属性来指定其中的一个。如:“SRC=pic.gif”,“Ordinal=3”标记的是 SRC的值是“pic.gif”的第3张图片
返回值:
成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)
生成表单的POST或GET请求,与上下文无关
语法:
int web_submit_data( const char *StepName, const char *Action, <List of Attributes>,
ITEMDATA, <List of data>, [ EXTRARES, <List of Resource Attributes>,]LAST);
//Action:Form表单中的action属性,即提交数据时执行操作的http地址
//List of Attributes:
//Method:表单提交方法:POST或GET(默认值:POST)
//EncType:编码方法
//EncodeAtSign:以其ASCII表示法编码“@”符号。Yes或No。
//TargetFrame:包含当前链接或资源的帧的名称。
//Referer、Mode(HTTP/HTML)
//UserAgent:标识将执行该步骤的浏览器以外的组件
//ITEMDATA:数据域和属性的分隔符
//List of data:表单提交的内容,上下文无关,格式为:“name=name1”, “value=value1”, ENDITEM,
//EXTRARES:数据域和资源属性列表分隔符
//List of Resource Attributes:资源属性列表
返回值:
成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)
示例:
web_submit_data("reservations.pl",
"Action=http://127.0.0.1:1080/cgi-bin/reservations.pl",
"Method=POST",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/cgi-bin/reservations.pl?page=welcome",
"Snapshot=t9.inf",
"Mode=HTML",
ITEMDATA,
"Name=advanceDiscount", "Value=0", ENDITEM,
"Name=depart", "Value=PEK", ENDITEM,
"Name=departDate", "Value=05/28/2022", ENDITEM,
"Name=arrive", "Value=SHA", ENDITEM,
"Name=returnDate", "Value=05/29/2022", ENDITEM,
"Name=numPassengers", "Value=1", ENDITEM,
"Name=seatPref", "Value=Window", ENDITEM,
"Name=seatType", "Value=First", ENDITEM,
"Name=.cgifields", "Value=roundtrip", ENDITEM,
"Name=.cgifields", "Value=seatType", ENDITEM,
"Name=.cgifields", "Value=seatPref", ENDITEM,
"Name=findFlights.x", "Value=65", ENDITEM,
"Name=findFlights.y", "Value=5", ENDITEM,
LAST);
提交表单,此函数可能必须在前一个操作的上下文中执行。录制时会判断浏览器中是否有cache的内容,如果有web_submit_form函数中只保存和缓存不相同的数据。其他同web_submit_data。
发送自定义的http请求。
语法:
Int web_custom_request (const char *RequestName, ,[EXTRARES, ,] LAST );
//List of Attribute:支持属性如下:
//URL:页面地址
//Method:提交方式,如POST/GET
//TargetFrame
//EncType:编码类型
//RecContentType:响应头的内容类型
//Referer
//Body:请求体
//RAW BODY
//BodyFilePath:作为请求体传送的文件的路径
//Resource、ResourceByteLimit、Snapshot、Mode
//ExtraResBaseDir
//UserAgent:用户代理
//Binary
//ContentEncoding:指定请求体的使用指定的方式进行编码
//EXTRARES:分隔符
返回值:
成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)
使用示例:
web_custom_request("login",
"URL=https://www.xxxx.com/login",
"Method=POST",
"RecContentType=application/json;charset=UTF-8",
"Referer=https://exam.cata.org.cn/",
"Mode=HTTP",
"EncType=application/json",
"body={\"code\":\"1q2w\",\"password\":\"xxxx\",\"str\":\"xxxxxx\",\"username\":\"xxx\"}",
LAST);
从HTML页面中查找指定的文本字符串
语法:
int web_find( const char *StepName, <Attributes and Specifications list>, char *searchstring, LAST);
//支持的属性:
//Frame:多Frame时,定义要查找的Frame范围
//Expect:定义检查成功的标准:found(默认)/notfound
//Matchcase:搜索是否区分大小写
//Repeat:第一次发现待查字符串时,是否继续搜索。yes/no
//Report:指定那种情况下在执行日志中显示检查结果:success/failure/always(默认)
//Onfailure:检查失败后,Vuser是否终端,设为abort时,运行时中的error-handling设置不生效,脚本中断;未指定该值,则error-handling设置生效
//RightOf:要查找的字符串右边内容
//LeftOf:要查找的字符串左边内容
//Searchstring:需要查找的字符串,格式为"What=stringxxx",不区分大小写
返回值:
成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)
示例:
web_find("find_string",
"RightOf=aaa",
"LeftOf=bbb",
"What=name",
LAST);
//注意:1、该函数在请求的页面内容完全显示出来以后在页面中查找,故需写道打开页面的动作之后;
//2、必须启用内容检查选项
从HTML页面中查找指定的文本字符串
语法:
int web_reg_find(const char *attribute_list, LAST);
//attribute_list:通过Name=Value传参,Text/TextPfx/TextSfx必须有一个,其他属性可选。
//Text:要搜索的字符串,肥哦那个;
//TextPfx:要搜索的字符串的直接前缀
//TextSfx:要搜索的字符串的直接后缀
//Search:搜索的范围:Headers、Body(默认)、Noresource(仅在HTML请求体中搜索,不含头和资源)、ALL
//SaveCount:匹配个数,若指定了该值,且未使用Fail,无论是否找到,检查不会失败
//Fail:设置检查函数在什么状态下失败,Found/NotFound(默认)
//ID:日志文件中标识此函数的一个字符串
//RelFrameId:相关联的FrameId
返回值:
成功时返回LR_PASS (0),失败时返回 LR_FAIL (1)
示例:
示例1:
web_reg_find("Fail=NotFound",
"Search=Body",
"SaveCount=1",
"Text=Web Tours",
LAST
);
示例2:
Action()
{
web_reg_save_param("userSession",
"LB=name=\"userSession\" value=\"",
"RB=\"/>",
LAST);
web_url("index",
"URL=http://127.0.0.1:1080/WebTours/index.htm",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTML",
LAST);
web_reg_find("Text=Welcome",
"SaveCount=WelcomeCount", //记录Text出现的次数
LAST);
web_submit_data("login.pl",
"Action=http://127.0.0.1:1080/cgi-bin/login.pl",
"Method=POST",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/cgi-bin/nav.pl?in=home",
"Snapshot=t4.inf",
"Mode=HTML",
ITEMDATA,
"Name=userSession", "Value={userSession}", ENDITEM,
"Name=username", "Value=jojo", ENDITEM,
"Name=password", "Value=bean", ENDITEM,
"Name=JSFormSubmit", "Value=on", ENDITEM,
"Name=login.x", "Value=51", ENDITEM,
"Name=login.y", "Value=10", ENDITEM,
LAST);
lr_output_message(lr_eval_string("{WelcomeCount}"));
if (atoi(lr_eval_string("{WelcomeCount}")) > 0){
//atoi:将字符串转化为整型
lr_output_message("Login success!");
} else {
lr_output_message("Login failure!");
}
return 0;
}
//运行结果:
//2
//Login success!
web_find和web_reg_find对比:
1、web_find必须开启内容检查才可生效;web_reg_find不必
2、web_find在返回页面中查;web_reg_find在缓存中查
3、web_find效率较低
4、总结:更推荐使用web_reg_find
将请求的动态数据信息保存到参数,一般用于参数关联
语法:
int web_reg_save_param (const char *ParamName, <List of Attributes>, LAST);
//ParamName:参数名,将关联的文本字符串保存在该参数
//List of Attributes:
//Convert:可选,HTML_TO_URL(将HTML编码数据转换为URL编码数据格式)/HTML_TO_TEXT(将HTML编码数据转为纯文本数据格式)
//IgnoreRedirections:可选,Yes时,不搜索重定向(300-303、307)返回的信息
//LB:必选,参数左边界
//RB:必选,参数右边界
//NOTFOUND:可选,当边界未找到时,生成一个空串,error(当边界未找到时,报错);warning(未找到时,设置参数计数为0,并继续执行脚本)
//ORD:可选,匹配的序号或出现的次数,默认为1,设置为ALL,则将参数值保存在数组中
//Search:可选,搜索范围:Headers、Body、ALL
//SaveLen:找到的值的字符按串长度(在指定偏移量中),默认-1,表示到字符串末尾
//SaveOffSet:找到的值的子字符串的偏移量,默认为0,从搜索到的字符串中取子串。
示例:
//设置userSession参数关联
web_reg_save_param("userSession",
"LB=name=\"userSession\" value=\"",
"RB=\"/>",
LAST);
//打开WebTours主页
web_url("index.htm",
"URL=http://127.0.0.1:1080/WebTours/index.htm",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTML",
LAST);
注意:在关联期间可以捕获的参数的最大长度的默认值为256个字符,如果需要使用到超过256字符的关联参数,需要使用web_set_max_html_param_len函数增加最大有效长度:
web_set_max_html_param_len("1024"); //login_token参数超长,需要增加最大长度
web_reg_save_param("login_token", //login_token参数长度超过256
"LB={\"token\":\"",
"RB=\",\"",
"Search=Body",
LAST);
将指定请求头添加到下一个HTTP请求中,如:
web_add_auto_header("content-type", "application/json;charset=UTF-8");
将指定的标头添加到所有后续HTTP请求中,如:
web_add_auto_header("token", "xxxxxx"); //后面的请求都自动带上token
添加了之后,后续所有请求请求头中都会带token。
为性能分析标记事务的开始,具体如下:
int lr_start_transaction (const char * transaction_name);
//transaction_name:事务名
//如:lr_start_transaction("tran01_login");
为性能分析标记事务的结束,具体如下:
int lr_end_transaction (const char * transaction_name,int status);
//status:事务的结束状态:共有LR_PASS(通过)、LR_FAIL(失败)、LR_AUTO(自动)、 LR_STOP(暂停)
//如:lr_end_transaction("tran01_login",LR_AUTO);
设置集合点,具体如下:
int lr_rendezvous(const char * rendezvous_name);
//rendezvous_name:集合点名称
//如:lr_rendezvous("旅客值机");
设置思考时间,单位秒,如:
lr_think_time(22);
返回脚本中一个参数的当前值:
格式:lr_eval_string("{参数名}");
将以NULL结尾的字符串保存到参数中
lr_save_string( const char *param_value, const char *param_name)
//param_value:指定字符串值
//param_name:参数名
//如:将字符串value1保存至参数name1:
//lr_save_string("value1", "name1");
示例如下:
Action()
{
lr_save_string("value1","name1");
lr_output_message("name1 is:%s",lr_eval_string("{name1}"));
lr_save_string(lr_eval_string("{name1}"),"name2");
lr_output_message("name2 is:%s",lr_eval_string("{name2}"));
return 0;
}
//运行结果:
//name1 is:value1
//name2 is:value1
将变长字符串保存到参数中
int lr_save_var (const char * param_value,unsigned long constvalue_len,unsigned long const options,const char *param_name);
//param_value:待分配给参数的值。
//value_len:值的长度(以字节为单位)。
//options:参数选项,当前为0。
//param_name:参数名称。
示例如下:
Action()
{
lr_save_string("abcde12345","string1");
lr_output_message(lr_eval_string("{string1}"));
lr_save_var(lr_eval_string("{string1}")+3, 4, 0, "string2");
//从string1下标3处开始截取,截取长度为4,将结果赋值给string2
lr_output_message(lr_eval_string("{string2}"));
return 0;
}
//运行结果:
//abcde12345
//de12
将当前日期和时间保存到参数中
void lr_save_datetime(const char *format, int offset, const char *name);
//format:格式化信息
/*
%a 星期几的简写
%A 星期几的全称
%b 月分的简写
%B 月份的全称
%c 标准的日期的时间串
%C 年份的后两位数字
%d 十进制表示的每月的第几天
%D 月/天/年
%e 在两字符域中,十进制表示的每月的第几天
%F 年-月-日
%g 年份的后两位数字,使用基于周的年
%G 年分,使用基于周的年
%h 简写的月份名
%H 24小时制的小时
%I 12小时制的小时
%j 十进制表示的每年的第几天
%m 十进制表示的月份
%M 十时制表示的分钟数
%n 新行符
%p 本地的AM或PM的等价显示
%r 12小时的时间
%R 显示小时和分钟:hh:mm
%S 十进制的秒数
%t 水平制表符
%T 显示时分秒:hh:mm:ss
%u 每周的第几天,星期一为第一天 (值从0到6,星期一为0)
%U 第年的第几周,把星期日做为第一天(值从0到53)
%V 每年的第几周,使用基于周的年
%w 十进制表示的星期几(值从0到6,星期天为0)
%W 每年的第几周,把星期一做为第一天(值从0到53)
%x 标准的日期串
%X 标准的时间串
%y 不带世纪的十进制年份(值从0到99)
%Y 带世纪部分的十制年份
%z,%Z 时区名称,如果不能得到时区名称则返回空字符。
%% 百分号
*/
//offset:时间偏移量
/*
DATE_NOW(现在的日期)
TIME_NOW(现在的时间)
ONE_DAY(一天的时间)
ONE_HOUR(一小时的时间)
ONE_MIN(一分钟的时间)
*/
//name:保存的参数名
示例如下:
Action()
{
lr_save_datetime("%Y-%m-%d",DATE_NOW+3*ONE_DAY,"threeDay");
lr_output_message(lr_eval_string("{threeDay}"));
return 0;
}
//保存3天后的日期到参数threeDay中,如今天为20220606,运行结果如下:
//2022-06-09
将消息打印到输出窗口
脚本增强时,为了对返回的数据做处理,经常会使用c语言字符串处理函数,常用的函数总结如下:
Loadrunner中常用的C数据类型包括整型和字符串,定义方法示例如下:
注意:定义数据必须要写在脚本的最前面,否则会报错。
char str1[] = "aabbcc"; //定义后需要即刻赋初值
char * str2, str3[10], str4[10]; //定义字符串str2;str3,str4长度为10
long num1 = 12345678; //定义长整型num1,并赋初值
int num2; //定义整型num2
str2 = "112233"; //给str2赋值
num2 = 123; //给num2赋值
lr_output_message("str1 is %s", str1);
lr_output_message("str2 is %s", str2);
itoa(num1,str3,10); //取num1值,转为10进制的字符串
itoa(num2,str4,16); //取num2值,转为16进制的字符串
lr_output_message("str3 is %s", str3);
lr_output_message("str4 is %s", str4);
运行结果如下:
/*
str1 is aabbcc
str2 is 112233
str3 is 12345678
str4 is 7b
*/
将格式化的字符串输出到目标字符串(一般为数组)
sprintf( char *string_buffer, const char *format_string[, args] );
//string_buffer:目标字符串(一般为数组)
//format_string:一个或多个格式化字符
//args:一个或多个可选的打印参数
示例如下
int num1 = 001;
char str1[20], * str2 = "abc";
sprintf(str1, "%d+%s", num1, str2);
lr_output_message("str1 is %s", str1);
//运行结果:str1 is 1+abc
返回字符串中首次出现子串的地址。
char *strstr(const char *str1, const char *str2);
//str1:被查找的目标
//str2:要查找的对象
//运行结果:若str2为str1的字串,则返回str2首次出现地址,若不是,则返回NULL
示例如下:
char * str1 = "abcdefg";
char * str2 = "cde";
char * str3 = "cdf";
lr_output_message("substr2 is %s", strstr(str1, str2));
lr_output_message("substr3 is %s", strstr(str1, str3));
lr_output_message("substr4 is %s" ,(char *)strstr(str1, str2));
if (strstr(str1, str3) == NULL) {
lr_output_message("fail!!!");
}
//运行结果如下:
/*
substr2 is cdefg
substr3 is (null)
substr4 is cdefg
fail!!!
*/
连接两个字符串。
char *strcat ( char *to, const char *from );
//将from连接到to后,连接后的字符串值赋给to
示例:
char str1[] = "abc"; //注意不能定义为char *str1 = "abc";这种方式定义的字符串不可变
char * str2 = "def";
strcat(str1, str2);
lr_output_message(str1);
//运行结果:abcdef
连接两个字符串,最多n位,如:
char str1[10] = "abcdef";
char str2[10] = "123456";
strncat(str1, str2 ,3);
lr_output_message(str1);
//运行结果:abcdef123
复制一个字符串到另一个字符串
char *strcpy(char *dest, const char *source)
示例:
char str1[100];
strcpy(str1, "abcdef");
lr_output_message(str1);
//运行结果:abcdef
复制一个字符串到另一个字符串,最多复制n个字符,示例如下:
char str1[20],str2[20];
strcpy(str1, "abcdef");
strncpy(str2, str1, 3); //str1的前3个字符复制到str2
lr_output_message(str1);
lr_output_message(str2);
lr_output_message((char *)strncpy(str2, str1, 3));
//运行结果
/*
abcdef
abc
abc
*/
返回字符串中指定字符后的字符串,未找到则返回NULL
char *strchr(const char *string, int c)
示例:
char *str1 = "abcdecfg";
char *str2, *str3, str4;
str2 = (char *)strchr(str1, 'c');
//strchr表示第一个出现的字符
//不能写成"c",单引号引起的字符代表整数,双引号为字符串
//(char *)为强制类型转换,将后面表达式强制转为char*类型
lr_output_message("str2 is %s", str2);
str3 = (char *)strrchr(str1, 'c');
//strrchr表示最后一个出现的字符
lr_output_message("str3 is %s", str3);
str4 = strchr(str1, 'm');
lr_output_message("str4 is %s", str4);
//运行结果:
/*
str2 is cdecfg
str3 is cfg
str4 is (null)
*/
比较字符串
int strcmp ( constchar *string1, const char *string2 );
int stricmp ( const char *string1, const char *string2 );
//strcmp大小写敏感,stricmp大小写不敏感
//string1 < string2时,返回-1
//string1 > string2时,返回1
//string1 = string2时,返回0
示例:
char *str1 = "AAA";
char *str2 = "aaa";
lr_output_message("result is %d", strcmp(str2, str1));
lr_output_message("result is %d", strcmp(str1, str2));
lr_output_message("result is %d", stricmp(str1, str2));
//运行结果:
/*
result is 1
result is -1
result is 0
*/
将字符串转为小写
char *strlwr(char *string);
示例:
char str1[10] = "AAA";
strlwr(str1);
lr_output_message(str1);
//运行结果:aaa
返回字符串长度,如:
char str1[10] = "abcdef";
lr_output_message("str1 length is %d", strlen(str1));
//运行结果:str1 length is 6
判断数据类型或者表达式长度,如:
char str1[123];
lr_output_message("str1 size is %d", sizeof(str1));
lr_output_message("int size is %d", sizeof(int));
lr_output_message("long size is %d", sizeof(long));
lr_output_message("char size is %d", sizeof(char));
//运行结果如下:
/*
str1 size is 123
int size is 4
long size is 4
char size is 1
*/
将某一块内存中的内容全部设置为指定的值,通常为新申请的内存做初始化工作
void *memset(void *str, int c, size_t n)
如:
char str1[20];
strcpy(str1, "abcdefg");
memset(str1, '#',3);
lr_output_message(str1);
//运行结果:###defg
实际初始化内存使用方法如下:
char str1[20];
strcpy(str1, "abcdefg");
memset(str1, 0, sizeof(str1));
//也可以写成:memset(str1, '\0', sizeof(str1));
lr_output_message("str1 is %s", str1);
//运行结果:str1 is