关联函数:
该函数的功能很强大:
1、用#代表任何数字
2、使用^代表通配符
3、获取匹配到的第几个值
4、找不到时报error还是报警,建议报警,这样程序还能继续执行
5、搜索范围,建议选择全部
6、类似 java中的substring方法,从第10个字符开始截取到第50个字符
7、忽略重定向
关联数组
要实现的一个需求是进入csdn首页,能够随机点击左侧导航栏进入到相应的栏目中。
比如进入程序人生:
链接是https://www.csdn.net/nav/career,每个栏目的域名相同,后面部分不同,要从首页的源码中查看每个栏目后面的部分信息,然后提取到一个数组中:
脚本已经录制完成,在脚本中添加步骤添加函数:
要给 引号加转义符
打印:
lr_output_message(lr_eval_string("{getitems}"));
因为是数组所以打印结果是:
打印出第二个:
lr_output_message(lr_eval_string("{getitems_2}"));
获取全部的值:
//获取数组的长度的函数lr_paramarr_len("getitems")
//lr_paramarr_idx("getitems",i)根据下标索引获取数组中的数据
for(i=1;i<=lr_paramarr_len("getitems");i++){
lr_output_message(lr_paramarr_idx("getitems",i));
}
随机获取一个值
r_output_message(lr_paramarr_random("getitems"));
使用sprintf给一个变量赋值:
//sprintf(myurlstring,"{getitems_%d}",i); 是把"{getitems_%d}"的值赋值给myurlstring,myurlstring是char数组,必须用数组才行要不然会报错,并打印出来
for(i=1;i<=lr_paramarr_len("getitems");i++){
sprintf(myurlstring,"{getitems_%d}",i);
lr_output_message(lr_eval_string(myurlstring));
}
//保存参数的函数lr_save_string(第一个参数是参数值,第二个参数是参数名称可以随意设置),这个参数testurl可以被下面的函数使用
lr_save_string(lr_paramarr_random("getitems"),"testurl");
lr_output_message(lr_eval_string("{testurl}"));
拼接参数并关联应用到下面的函数中
//拼接要访问的url
sprintf(askurlstring, "https://www.csdn.net%s", lr_paramarr_random("getitems"));
lr_output_message(askurlstring);//打印变量查看变量有没有拼接成功
//把拼接的url保存到一个变量中这样才能被下面的函数应用
lr_save_string(askurlstring,"askurlstring");
//访问拼接的url
web_url("career",
"URL={askurlstring}",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t113.inf",
"Mode=HTML",
LAST);
下面的代码实现的是打开csdn的首页,然后随机选中一个栏目,再进入栏目中随机点击一篇文章。
Action_Allcode()
{
//变量的声明必须放到前面,开始执行代码后不能声明变量
//声明一个变量,必须放到首行这个位置才行
int i;
//声明一个char变量,用来存放拼接的URL地址
char* urlstring="https://www.csdn.net";
//声明一个长度为1024的char数组,下面没有用到
char myurlstring[1024];
//声明一个长度为1024的char数组,应用到下面的url中
char askurlstring[1024];
//声明一个char变量,用于存放栏目的名称
char *askurltitle;
web_set_sockets_option("SSL_VERSION", "TLS1.1");
web_add_cookie("Hm_ct_6bcd52f51e9b3dce32bec4a3997715ac=6525*1*10_19814196820-1526528487575-539618; DOMAIN=www.csdn.net");
web_add_cookie("uuid_tt_dd=10_19814196820-1526528487575-539618; DOMAIN=www.csdn.net");
web_add_cookie("dc_tos=ptyfw6; DOMAIN=www.csdn.net");
web_add_cookie("dc_session_id=10_1526528487575.516753; DOMAIN=www.csdn.net");
web_add_cookie("Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac=1561970163; DOMAIN=www.csdn.net");
web_add_cookie("TY_SESSION_ID=47e865f3-1857-4c8d-8791-ab14094f7f4f; DOMAIN=www.csdn.net");
web_reg_save_param("getitems",
"LB/ALNUMLC=<li class=\"\"><a href=\"",
"RB=\">",
"Ord=ALL",
LAST);
// web_reg_save_param_regexp(
// "ParamName=getitems",
// "RegExp= <li class=\"\"><a href=\"/nav/.*\">.*</a></li>",
// "Group=0",
// SEARCH_FILTERS,
// LAST);
web_url("www.csdn.net",
"URL=https://www.csdn.net/",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t109.inf",
"Mode=HTML",
EXTRARES,
"Url=/images/icon_close_big.png", ENDITEM,
"Url=/images/is_top_big.png", ENDITEM,
"Url=/favicon.ico", "Referer=", ENDITEM,
"Url=/images/icon_close_hover_big.png", ENDITEM,
LAST);
// //打印出数组的第二个值,数组的下标是从1开始的,所以第二个是2
// lr_output_message(lr_eval_string("{getitems_2}"));
//
//
// //获取数组的长度的函数lr_paramarr_len("getitems")
// //lr_paramarr_idx("getitems",i)根据下标索引获取数组中的数据
// for(i=1;i<=lr_paramarr_len("getitems");i++){
// lr_output_message(lr_paramarr_idx("getitems",i));
// }
//lr_paramarr_random("getitems")随机获取数组中的数据
// lr_output_message(lr_paramarr_random("getitems"));
//sprintf(myurlstring,"{getitems_%d}",i); 是把"{getitems_%d}"的值赋值给myurlstring,myurlstring是char数组,必须用数组才行要不然会报错,并打印出来
// for(i=1;i<=lr_paramarr_len("getitems");i++){
// sprintf(myurlstring,"{getitems_%d}",i);
// lr_output_message(lr_eval_string(myurlstring));
// }
//保存参数的函数lr_save_string(第一个参数是参数值,第二个参数是参数名称可以随意设置),这个参数testurl可以被下面的函数使用
// lr_save_string(lr_paramarr_random("getitems"),"testurl");
// lr_output_message(lr_eval_string("{testurl}"));
//拼接要访问的url
sprintf(askurlstring, "https://www.csdn.net%s", lr_paramarr_random("getitems"));
lr_output_message(askurlstring);//打印变量查看变量有没有拼接成功
//把拼接的url保存到一个变量中这样才能被下面的函数应用
lr_save_string(askurlstring,"askurlstring");
//多次迭代web_url的名称都一样,本来想实现名称要根据点击的url变化,web_url的名称不能用变量参数
//从字符串后面开始搜索'/',找到后直接截取包含'/'的后面的内容
askurltitle=(char *)strrchr(askurlstring,'/');
lr_save_string(askurltitle,"askurltitlestring");//保存到askurltitlestring中并打印查看结果
lr_output_message(lr_eval_string("{askurltitlestring}"));
//添加一个关联参数,从随机进入的栏目返回的结果中获取文章url
web_reg_save_param("articleurl",
"LB=data-track-view='{\"mod\":\"popu_459\",\"con\":\",",
"RB=,",
"Ord=ALL",
LAST);
//访问拼接的url
web_url("career",
"URL={askurlstring}",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t113.inf",
"Mode=HTML",
LAST);
// //打印输出获取到的articleurl值
// lr_output_message(lr_eval_string("{articleurl}"));
//
// //打印输出关联数组中的每一个值,并存到变量中
// for(i=1;i<=lr_paramarr_len("articleurl");i++){
// lr_output_message(lr_paramarr_idx("articleurl",i));
// }
//从访问的栏目url中随机取一个articleurl值并存到变量中
lr_save_string(lr_paramarr_random("articleurl"),"articleurlstring");
lr_output_message(lr_eval_string("{articleurlstring}"));
web_url("89089809",
"URL={articleurlstring}",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=https://www.csdn.net/nav/career",
"Snapshot=t118.inf",
"Mode=HTML",
EXTRARES,
"Url=https://www.csdn.net/favicon.ico", "Referer=", ENDITEM,
"Url=https://pagead2.googlesyndication.com/pagead/js/r20190626/r20190131/show_ads_impl.js", ENDITEM,
"Url=https://pagead2.googlesyndication.com/pub-config/r20160913/ca-pub-1076724771190722.js", ENDITEM,
"Url=https://www.googletagservices.com/activeview/js/current/osd.js?cb=%2Fr20100101", ENDITEM,
"Url=https://entry.baidu.com/rp/bwordcom?di=&user=&page_url=https%3A%2F%2Fblog.csdn.net%2FPx01Ih8%2Farticle%2Fdetails%2F89089809&logid=111&title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E7%AE%97%E6%B3%95%20-%20ConcurrentHashMap%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%20-%20%3Csdffdsfsdfdfs%3Esfsfsfsdfsdffds%3C%2FsdfsDS%3EFsd%20-%20CSDN%E5%8D%9A%E5%AE%A2&jsonp=baidu_bw_1562048038258", ENDITEM,
LAST);
return 0;
}
loadrunner还有其他的关于字符串的函数没有用到,可以参考:
https://www.cnblogs.com/qmfsun/p/4900562.html
事务
统计每一个请求或者每一批请求的响应时间,评估系统的处理速度。
统计事务的成功率:评估系统的稳定性。
自动事务有两个选项:按每一个操作action为一个事务、按每一个步骤为一个事务
保存action,创建controller场景,运行查看按每一个action为一个事务的执行情况:
运行完毕,点击事务响应时间查看结果,显示最大值、最小值、平均值等信息:
再设置按每一个步骤为一个事务查看响应时间:
可以不勾选自动事务,也可以勾选,不冲突,手动设置事务:
手动定义三个事务,进入controller场景中运行查看事务响应时间:
在事务中写其他代码也会产生浪费时间,可以把这部分时间减去,代码如下:
Action_wastertime()
{
double time_elapsed;//定义double类型的变量,用来存储时间
merc_timer_handle_t timer;//定义timer 用来接收开始时间
int i;//定义一个整型变量,可以做循环
char b[50];//定义一个char数组,用来把double类型的时间转换成char类型,可以打印出来
lr_start_transaction("testing");//开始事务
web_url("www.baidu.com",
"URL=http://www.baidu.com/",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTML",
EXTRARES,
"Url=/favicon.ico", "Referer=", ENDITEM,
LAST);
timer=lr_start_timer();//开始计算时间
for(i=0;i<1000;i++){
lr_output_message("消耗时间");
}
time_elapsed=lr_end_timer(timer);//结束计算时间
lr_wasted_time(time_elapsed*1000);//从毫秒换算成秒后加入浪费时间中
lr_end_transaction("testing", LR_AUTO);//结束事务,事务会自动减去浪费时间
sprintf(b,"%2f",time_elapsed);//把double类型的浪费时间变量转换成char类型数组,并赋值给b变量
lr_save_string(b,"time_elapsedpara");//把变量转换成参数
lr_output_message("浪费的时间是:%s",lr_eval_string("{time_elapsedpara}"));//打印浪费时间
return 0;
}
检查点
loadrunner结束事务中的LR_AUTO只是通过状态码来区分事务是否成功,比如登录时密码错误,能正常返回界面告诉用户密码错误请重新输入密码,对loadrunner来说检测到服务器返回是200的状态码,所以检测到事务成功了,但是从业务角度来说并没有成功,所以需要通过检查点来判断事务是否真正的成功。
插入函数,函数要放到请求之前,可以按文本去查也可以按字符串去查:
根据统计次数大于等于1判断请求成功。
也可以使用函数自定义修改事务的状态:
lr_set_transaction_status_by_name(LR_FAIL,"protectcar");
lr_set_transaction_status_by_name(LR_PASS,"protectcar");
勾选和不勾选这个对注册检查点web_reg_find()函数无效。
思考时间
思考时间就是模拟用户暂停发请求的时间,为了让loadrunner模拟真实的场景,是需要使用思考时间的。
思考时间定为5秒,按最小值50%,最大值200%随机选取思考时间:
集合点01
集合点适用场景是并发测试,主要关注大用户量的并发。分为两种情况:1、所有用户都在并发请求,请求的模块可能是不同的 2、所有用户都在提交同一个请求。
集合点是不能模拟真实场景,至少不能很绝对的模拟,因为不可能达到绝对的并发,服务器接收到请求也是有先后顺序的,处理请求也是需要队列处理,所以达不到绝对意义的并发。
并发测试:相对严格的并发,并没有绝对严格的并发测试。并发测试属于压力测试的一个子集。
压力测试:关注系统最大的瓶颈是多少,系统在什么时候会发生崩溃,系统在什么样的场景下会发生崩溃。并发测试属于压力测试的一个子集。做压力测试时不需要设置思考时间,直接压测就行。
负载测试:评估性能指标,当系统有100个人、1000个人、10000个人访问的时候系统的处理情况是什么,cpu、内存、数据库等的表现是什么样的。
稳定性测试:系统在标准用户数、最佳状态下的长时间的一种运行,比如cpu利用率在70%~80%左右,资源充分利用并且还有上升的空间的一种状态。
系统的最大用户数:是指某一个指标出现极限时的一种状态,不可能所有指标都出现极限状态,比如内存到达极限,或者cpu到达极限等等。
容量测试:模拟系统长时间运行后的性能状态,目前流行的关系型数据库中都有自己的存储引擎,比如使用索引、关键字等可以很快速的查询出数据。比如查询1000条和查询1亿条时数据库的处理速度是什么样的,sql语句是否要优化等的测试。
集合点02
先创建一个事务,再创建一个集合点就会出现事务统计的时间不准确的情况,因为因为集合点是要等所有用户到达后统一去发起下一个请求,所以集合点中有一个等待的时间,暂停的时间是要统计到事务的时间中的,所以集合点不能放到事务里面要放到外面,这样事务统计的时间才准确。
模拟场景一:集合点放在事务里面,查看统计时间
每隔10秒会释放5个用户:
事务的响应时间:
模拟场景二:集合点放在事务外面,查看统计时间
前5秒先运行5个用户,其他用户等待状态:
到时间再释放5个
全部释放开始运行
事务响应时间:
可见最大时间比上一个场景的时间小,最后一个运行时间也变小了
模拟场景三:上两个场景持续运行时间太长,导致一些虚拟用户迭代失败,现在改成持续运行3分钟
可以看出后到集合点的用户已用时间短:
了解场景中的集合
每隔10秒释放5个用户,在场景中设置当运行的用户到达时就可以开始发出请求
第一个选项是说当所有的虚拟用户都到达时才发出请求,与设置的每隔10秒释放5个用户是有冲突的,这样每隔10秒释放5个用户,释放后用户要等待,等待所有的用户都被释放后才发出请求
vuser之间的超时值设置为10秒,是说当选中第一个选项时,在用户还没到达但是已经超时了就不再等待用户了,直接发出请求,这样不浪费时间
这个配置是说当有1个用户到达时就发出请求:
一般使用默认的配置就可以:
模拟场景四,设置等所有用户到达时才发起请求
不太明显。
函数总结:
lr_output_message(lr_eval_string("{参数名称}"))
打印结果是否获取成功
web_reg_save_param_ex()关联参数
web_reg_save_param()关联函数
lr_paramarr_len("getitems")获取数组的长度
lr_paramarr_idx("getitems",i)根据下标索引获取数组中的数据
lr_paramarr_random("getitems")随机获取数组中的数据
sprintf(myurlstring,"{getitems_%d}",i); 是把"{getitems_%d}"的值赋值给myurlstring,myurlstring是char数组,必须用数组才行要不然会报错
lr_save_string(lr_paramarr_random("getitems"),"testurl");//lr_save_string(第一个参数是参数值,第二个参数是参数名称可以随意设置)
web_reg_find检查点函数,要放到请求之前
lr_log_message("要打印的日志内容");
lr_get_attrib_string("在运行时设置中其他属性的参数名称")
web_custom_request()发送自定义请求,与web_url,web_sumbit_data()一样
web_add_header("content_type","application/json")增加请求头,如果接口中需要在header中传token,也可以用这个函数把token加到请求头中,因为token会随着登录的变化而变化,所以需要先使用关联函数获取到token然后再把token放到header中供下面的接口使用。
web_get_int_property(HTTP_INFO_RETURN_CODE)获取上一个请求的返回码,返回类型是int,可以赋值给变量,用于判断请求结果。
lr_load_dll("md5.dll");加载md5.dll 加密方法放到loadrunner的bin目录下,直接引用dll,然后就可以直接用他里面的加密方法了。测试中也可能是其他的加密方法,可以找开发要。
lr_convert_string_encoding()转换字符类型,下面会讲解具体使用方法。
web_reg_save_param_xpath()接口返回内容是xml时可以通过这个关联函数获取参数,查询路径是xpath路径
web_reg_save_param_xpath(
"ParamName=error_code",
"QueryString=/response/error_code",
SEARCH_FILTERS,
LAST);
事务相关函数
lr_start_transaction("testing");//开始事务
timer=lr_start_timer();//开始计算时间
//浪费时间的代码
time_elapsed=lr_end_timer(timer);//结束计算时间
lr_wasted_time(time_elapsed*1000);//从毫秒换算成秒后加入浪费时间中
lr_end_transaction("testing", LR_AUTO);//结束事务,事务会自动减去浪费时间
自定义事务状态函数:
lr_set_transaction_status_by_name(LR_FAIL,"protectcar");
补充函数总结:
lr_get_attrib_string("在运行时设置中其他属性的参数名称")
lr_continue_on_error(1);//出现错误继续执行
//中间可以放要执行的代码
lr_continue_on_error(0);//出现错误不继续执行,关闭开关,与运行时设置的遇到错误继续执行是一样的。
web_get_int_property(HTTP_INFO_RETURN_CODE)获取上一个请求的返回码
web_add_header();
登录成功后获取token,下面其他的接口都需要在header中添加token,所以在下面所有的接口前增加web_add_header增加token。
lr_convert_string_encoding("A", NULL, LR_ENC_UTF8, "stringInUnicode");
参数分别是sourceString,fromEncoding,toEncoding ,paramName
fromEncoding和toEncoding有三种:LR_ENC_SYSTEM_LOCALE 、LR_ENC_UTF8 、LR_ENC_UNICODE
该函数返回0是转换成功,返回-1是转换失败。
下图中是把参数转换字符类型,打印结果后面会带\x00