在用LoadRunner 做性能测试的过程中,编写脚本是 一项非常重要的工作。不夸张地说,一个脚本的好坏关系到性能测试的成败。“ 工欲善其事,必先利其器” , 在本章我们将学习LoadRunner 脚本语言以及脚本的开发技巧。
下面 我 们 要 通 过 一个 H T T P 协 议 的 脚 本 实 例 ,来熟悉一下vU的脚本语 言 和 脚本结构。
首先,我们还是启动VU,在“File” 菜单中选择“New” ,然后在弹出的协 议列表中选择Web (HTTP/HTML) 协议。 新建脚本后,会弹出一个录制选项设置对话框,在这个对话框里可以设置录 制的内容和方式,我们在“URL Address里 输入htp:/newrours.demoaut.com/, 然后单击“OK〞按钮。
“http://newtours.demoaut.com/ ” 是HP Mercury 提供的演示网站,在访问之前, 确保你的计算机能够连接上Internet。你也可以输入其他网址,这样 LoadRunner 就会自动捕捉浏览器访问网络的操作,记录生成 一个最简单的HTTP脚本。 记住,在页面完全展现后,单古“Stop” 按钮,停止录制,手是你就可以看 到VU生成了如图4-3 所示的一个脚本。 请注意,在VU左边导航栏中,我们看到有三个LoadRumer 的框架西数,即 vuser_inito 、ActionO和user_end0。这 三个函数存在于任何Vuser 类型的脚本中, 这三个两数究竟有什么作用呢?下面我们 一一分析。vuser_init :顾名思义,这是虚拟用户的初始化西数,一般将用户初始化的操 作放在这里,如登录操作、分配内存等,而且在做vuserinit 的时候,Controller 的Vuser状态区域会显示initialize 状态。
Action : 是虚拟用户要做的业务。用户的业务操作,也就是测试内容的主体。在VU里设置选代循环选项时,只有Action 会生效,被重复运行,而init 和end 部分则在脚本的运行过程中只会运行一次。
v u s e r _ e n d : 与 v u s e r _ i n i t 相 对 应 ,v u s e r _ e n d 做 收 尾 工作 。 在 v u s e r _ i n i t 中 如 果 是登录,vuser_end 里就要做退出登录;在vuser_init 中如果是申请内存,比如使 用了malloc 函数,在vuser_end 中就应该是释放内存,使用free 函数。
当脚本启动时,这三个西数的运行顺序如图4-4 所示
实例
场景需求:我们需要对邮件系统做收发Mail 的负载测试,每个虚拟用户登录 邮件系统,收发50 封邮件,然后退出。
脚本设计:将登录邮件系统的操作放在vuser_init 函数中,收发Mail 的操作 放在 Action 中,退出邮件系统的操作放在 vuser_end 中,然后将迭代循环次数设 为50 。这样设计的脚本,会先运行vuser_init 一次,然后Actions 循环运行50 次, 再运行vuser end一次,脚本结束,达到了场景需求。
提 示 :在 处 理 使 用 J a v a 类 的 V u s e r 脚 本 时 ,所 有 的 代 码 都 将 置 于 A c t i o n s 类 中 而 A c t i o n s 类 同 样 包 含 了 三个 方 法 : i n i t 、 a c t i o n 和 e n d
在 V U 右 侧 脚 本 编 辑 框 中 ,我 们 看 到 的 是 A c t i o n 的 实 现 主 体 ,在 运 行 时 , A c t i o n 内的web_url 函数会被调用运行。
在LoadRunner 的脚本中,我们可以调用三种函数:
(1 vU通用函数, 一般以Ir开头,就如上面的Ir_starttransaction两数。
(2)协议相关西数,不同类型的Vuser 的函数一般以本协议类型开头。如 上面的脚本是Web (HTTP/HTML)类型的,web_url 就是一个协议函数,web 前缀说明它是属于Web HTTP 协议的,HTTP 协议函数还包括 web_list、 web_link.
( 3)语言相关西数。上面的VU脚本是用C语言写的,那么C语言的标准西 数或dll 都可以在这里被加载和使用。
一般地,vU 录制生成的脚本中包括前两种函数,如果要对脚本进行扩充,可 使用第三种“ 用户自定义函数” 开发,这是Java 脚本里经常做的事情,这 三种西 数一起构成了VU的API,如图4-5所示。这些API 组合起来的威力是强大的,尤性能测试从零开始一- LoadRunner 入门 其C语言的书展性和灵活性,能够保证我们开发出来不同网络协议的脚本,使得 VU 与Server 进行交互。
提示:在LoadRunner 早期版本中,VU只支持C语言,从8.0开始,VU的功 能进一步增强,开始支持Java、VB、VBScript 和JavaScript。 在这里,我们以C语言为例,介绍 一下LoadRunner的API。
4.1 C语言与LoadRunner脚本
在 默 认 情 况 下, Vugen的 自 动 脚 本 生 成 器 使 用 C 语 言 为大多数协议 创 建 Vuser 脚本。
在 本 节 我 们 主要 学 习一 下 L o a d R u n n e r 协 议 中 最 普 遍 支 持 和 使 用 的 C 脚 本。 这 是我们录制、开发LoadRumer 脚本所必须掌握的基本功,一旦我们掌握了C语言 的 奥 妙, 再 复 杂 强 大的 L o a d R u n n e r 脚 本 也 不 在 话 下。
LoadRunmer VU文持C语言,所有标准ANSI-C约定都适用于CVuser脚本, 包括控制流和语法。与在其他C程序中的操作类似,可以向脚本中添加注释和条 件语句。可以使用ANSI-C约定声明和定义变量。
C语言是一种结构化语言。它层次清晰,便于按模块化方式组织程序,易于 调试和维护。C语言的表现能力和处理能力极强。它不仅具有丰富的运算符和数 据类型,便于实现各类复杂的数据结构;它还可以直接访问内存的物理地址,进 行位(bit) 一级的操作。这种灵活性和高效率的优势同样也体现在LoadRunner 的 C 語言脚本里。
在这里,我们先介绍一 下C 语言的基本知识。
4.1.1 看不见的main
在C语言里,main 是主两数的函数名,表示这是一个主西数。每一个C源程 序都必须有,且只能有一个主函数(main 函数)。程序的执行;总是从main 函数开 始 , 完 成 对 其他 西 数 的 调 用 后 再 返 回 到 m a i n 。 比 如 下面 一 个 简 单 的 C 程 序:
#includexst dio.h>
my_action()
{
/★在屏幕輸出 大的hel1o world */
printf ("hello world");
}
int main()
{
my_action ();
return 0;
}
include 是預処理命令,其作用是将外部西数引用到本文件。
“1*” 是C 语言的注释开始符,一 个注释块是以“p*” 开头并以“*/” 结尾的 串。在“ *” 和“*” 之间的即为注释。程序编译时,不对注释做任何处理。注释 可出现在程序中的任何位置。注释用来向用户提示或解释程序的意义。在调试程 序中对暂不使用的语句也可用注释符括起来,使翻译跳过不做处理,待调试结束 后再去掉注释符。
本程序运行时,程序首先找到main 的入又,然后调用my_action0 函数,在 my_action0两数中向屏幕输出“hel lo world” 文字,my_actionO执行完后,回到 main,退出。程序执行结果如下,在屏幕上输出:
hello world
printf 的功能是向屏幕输出变量内容。在LoadRunner 的API 里也有一个和 printf 功能一样的函数:Ir_output_mesage,我们如果在VU 的vuser_init、Action
和vuser_end 中各加一条Ir_output_message 语句来标明自己所在,如下所示。 vuserinit 做 如 下修 改 :
vuser_init ()
{Ir_output_message ("I am in vuser_init!"); return 0;
]
Action()
{1r_output_message ("I am in action!"); return 0;
}
vuser_end ()
{Ir_output_message ('I am in vuser_end!"); return 0;
]
修改完牛后,单击“ 运行” 按扭,开始运行脚本,在输出窗又中会看到如下 结果:
Virtual User Script started
Starting action vuser_init.
Web Turbo Replay of LoadRunner 8 . 1 . 0 for WINXP; WebReplay8 b u i l d 5187
[MsgId: MMSG-271431
Run-Time Settings file: "C: \example\default.cg" [MsgId: MMSG-27141]
vuser_init.c(3): I am in vuser_init! Ending action vuser_init.
Running Vuser..
Starting iteration 1. Starting action Action.
Action.c(3): I am in action! Ending action Action.
Ending iteration .1
Ending Vuser.
Starting action vuser_end.
vuser_end.c(3): I am in vuser_end! Ending action vuser_end.
Vuser Terminated.
我们发现运行结果除了输出“Iaminvuser init”、“IaminAction”和“Iam in vuser end” 三条语句之外,还有其他一些输出语向不是我们控制输出的。它们 其实是Vuser 的框架10g,无论运行什么脚本,它们总会出现在输出窗又里,以提 示当前的运行状态。
Teration 是我们在LoadRunner运行时设置的Action迭代次数,默认为1。从 以上输出结果可以看出来,LoadRunner 的运行机制是先运行vuser_init,然后根据 设置的迭代次数运行 Acti on ,最后调用 vuser_end ,退出。
但在LoadRunner 脚本里,我们没有看到C 程序的入又main 函数,难道 LoadRunner 脚本不需要main 函数么?其实C 程序是不可能没有main 的,只不过 这个main 函数和LoadRunner 的实现细节一起己经被隐藏在后台,负责幕后操作, 而用户可见的是Load Runne r 给用户已经封装好的函数和接又。可以想象,LoadRunner 的实现是这样的:
/*用户可见部分*/
int vuser_init()
{
...// 用户填写初始化
}
int action ();
{
......//用户填写
}
int vuser_end ()
{
...// 用户填写
}
/*后台隐藏部分*/
int main()
{
/* 初始化脚本全局变量和环境,调用vuser_init */
vuser_init ();
/ *调用 Action */
Action () ;
/*调用 vuser_end*/
vuser_end ();
return 0;
}
提示:vuser_init、Action 和vuser_end 其实也是三个普通的西数,只是 LoadRunner 赋予了它们功能的含义。我们如果运行下面这段脚本,可能会看得 更 清楚 一些。
vuserinit()
{
//清除浏览器绥存
//登录
......
}
Action ()
{
//trade 事务开始
LI_start_transaction("trade");
//web_r eg_f ind 是预先注册一个查找,在后面的请求返回的结果内查找指定的字 符串,其中查找的左边界是LB,右边界是RB。如果出现了wel come 的字样,则设交易为成功, 否则为失败web_reg_find("TextCheckPoint", "NOTFOUND=ERROR","LB=Welcome to","RB= Manager",LAST);
1r_output_message ( "TextCheckPoint: %s" , 1r_eval_string("{TextCheckPoint}"));
//如果我们查找成功,那么脚本继续,如果查找失败,那么将会调用vuser_init ();If (stremp (Ir_eval_string (" (TextCheckPoint]"), "Debt") ==0)
{1r_log_message ("*****TextCheckPoint OK*******");
}
else
{Ir_end_transaction("trade", LRFAIL); 1r _1og_message("*******TextCheckPoint FAIL*******");//失败后,重新运行 vuser_init,清除缓存 vuser_init ();
}Ir_end_transaction("trade", LR_AUTO):
}
在上面的脚本中,vuser initO西数作为一个普通的函数,在Action 函数中被 调用。web_reg_find 和Ir_eval_string 这两个函数是LoadRunner 脚本常用的两个两 数,它们由LoadRunner 提供,我们将在后面的章节陆续为大家介绍。
4.1.2 全局变量与局部变量
C语言中的变量,按作用范围可分为全局变量和局部变量。局部变量也称为 内部变量。局部变量是在函数内作定义说明的,其作用域仅限于函数内,离开该 函数后再使用这种变量是非法的。例如:
Int m,n;
//m,n 定义在两数体外,为全局变量,作用域为整个文件,在main,f1,f2 两数中均可被使用
int f1(int a)/*函数f1*/
{
int b,c;
}
//a,b.c 为局部变量,作用域仅在f1西数体内,在f1西数之外再使用a,b,c 变量 就会报错int f2(int x) /*函数f2*/{int y,z;}
// x.y,z为 局 部 变 量 , 作用域仅在f2西数体内, 在 f2 函数之外再使用x,y,2 变 量
就会报错:
main()
{
m=4; n=5;
f1 (m);
f2 (n);
}
在LoadRunner中,vuser_init、Action、vuser_end 和上面例子中的f1、22函数 一样,也都是普通的西数。那么在vuser init 中定义的变量是局部变量,其作用域 在vuser_init 函数范围内,在vuserinit 函数外试图使用它就会发生错误。 假设我们有这样一个需求:用户在初始化的时候需要分配内存,在用户退出 的时候释放内存,那么下面的代码会出现错误:
vuser_init ()
{
char *p = (char *)malloc (1000*sizeof (char)) ;
}
Action ()
{
}
vuser_end ()
{
free (p);
}
这段代码是无法编译通过的,在vuser_init 中定义了指针p. 并为其用malloc 函数申请了100 个字节的内存空间,为了避免内存泄漏,用户试图在vuser_end 中 去释放又指向的内存空间,但fr ee( p) 的时候,由于变量p 是在vuse r i ni t 中定义的, 其作用域仅限于vuser_init 范围,离开了vuser_init. 在vuser_end函数中使用变量 p,就会报出“p 变量未被定义” 的错误。
那如何使vuser_init、Action 和vuser end能共同使用 一个变量呢?这就需要全 局变量了。 全局变量也称为外部变量,它是在两数外部定义的变量。它不属于哪一个函数,它属于一个源程序文件,其作用域是整个源程序。在函数中使用全局变量, 一般应作全局变量说明。
在使用LoadRunner的HTTP协议录制Web系统时,会生成 一个globals.h文 件,在这里定义的变量,相当于一个 LoadRunner 脚本的全局变量,可以在 vuser_init 、Action 、vuser_end 中被使用。
在globalsh 文件中添加变量定义:
# ifndef_GLOBALS_H
#define _GLOBALS_H
// 包含头文件
#include "Irun.h"
#include "web_api.h"
#include "1rw_custom_body.h"
// 包含全局变量
char *p;
# endif //_GLOBALS_H
在vuser_init 和vuser_end 中增加如下代码:
vuser_init ()
{p = ( char * ) mal1oc(10*sizeof(char)); // 为 p分配10个字节的内存空间 memset (p,'o',9);//将9个字节的空间全部赋值为0memset (p+9,'\0',1);//将第10个字节赋值成字符串的结束符"\0"
}
Action ()
{1r_output_message("%s", p); // 在Action中输出p指向 的 空 间 的 值}
vuser_end ()
{Ir_output_message( "%s",p ); //在vuser_end中 输 出 又 指 向 的 空 间 的 值free (p); //释放内存,与mal1oc 成对使用
}
f ree (p) :/ 1释放内存,与ma l 1oc 成对使用 这次脚本就能顺利编译通过和运行了。在globals.h中定义了一个char 类型的 指针p,然后在vuser_ini t 中使用malloc 两数为p 在堆中分配 了10 个字节的空间,性能测试从零开始一—LoadRunner 入门 并将此空问全部赋值为“0” 。因为p是全局变量,在wser_init、Action、vuser end 中都可使用,因此,在vuser_end 中用free 函数释放p 指向的内存空间,达到了我 们的目的。
执行脚本,输出结果如下:
Virtual User Script started
Starting action vuser_init.
Web Turbo Replay of LoadRunner 8.1.0 for WINXP;
WebReplay8 build 5187 [MsgId: MMSG-27143]
Run-Time Settings file: "C: \example\default.cfg" MMSG-27141]
Ending action vuser_init.
Running Vuser.
Starting iteration 1.
Starting action Action.
Action.c(3): 000000000 Ending action Action. Ending iteration .1 Ending Vuser...
Starting action vuser_end. vuser_end.c(3): 000000000 Ending a c t i o n vuser_end.
[MsgId:
Vuser Terminated.
我们可以看到vuser_init 中的字符串指针在Action 中也照样可以被使用,输出 值为000000000,然后在vuser_end 中p指针指向的内存被释放。
4.1.3 在LoadRunner脚本里灵活使用C语言
LoadRunner脚本支持C语言,因此标准C语言的规范都可在LoadRunner 里 得到应用,包括C 支持的数据类型、控制语句、文件处理等。
以 下是 一个简单的例 子。
假设我们要创建一个Web 脚本来测试电子购物站点。此站点上目前有10 种 商品,每种商品都具有均等的可能性被客户购买。另外,网站支持30 个用户在线75 购买。也就是说,我们要模拟30 个用户登录电子购物网站,然后购买同等数量的 商品。
针对这种场景,我们可以想到的有两种方法:第一种方法,录制10 个脚本, 每个脚本完成1种网上商品的订购,相应地,设置10个虛拟用户组(Vusergroup) , 每 组 三 个 虚 拟 用 户 (V u s e r ), 这 样 保 证 一 共 3 0 个 用 户 , 每 种 商 品 的 购 买 数 量 相 同。第二种方法,我们创建一个脚本,在脚本内部用程序实现分派同等数量的用 户去购买不同的商品。显然如果第二种能够实现,将会降低测试的复杂度, 脚本、 一个虛拟用户组即可完成工作。 下面,我们尝试使用第二种方法来实现脚本。
在实现脚本之前,我们需要了解以下几个C 语言的知识点:
(1 )指 针
指针是C语言中广泛使用的一种数据类型。运用指针编程是C语言最主要的 风格之一。利用指针变量可以表示各种数据结构:能很方便地使用数组和字符串; 并能像汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰 富了C语言的功能。学习指针是学习C语言中最重要的一环,能否正确理解和使 用指针是我们是否掌握C语言的一个标志。
在计算机中,所有的数据都是存放在存储器中的。一般把存储器中的一个字 节称为一个内存单元,不同的数据类型所占用的内存单元数不等,如整型量占2 个单元,字符量占1 个单元等。为了正确地访问这些内存单元,必须为每个内存 单元编上号。根据 一个内存单元的编号即可准确地找到该内存单元。内存单元的 编号也叫做地址。既然根据内存单元的编号或地址就可以找到所需的内存单元, 所以通常也把这个地址称为指针。
指针是C语言灵活高效的精髓,它是 一个特殊的变量,它里面存储的数值 被解释成内存里的一个地址。在西数调用的时候,指针作为形参,可以作为返 回值。
性能测试从零开始一L oadRunner 入门
(2 ) %运算符
%运算符是返回左值和右值相余的结果。比如10%3的结果为1,3%4 的结果 # 0.
(3 )switchicase 控制结构
switch/case 控制结构在C 语言中用来控制程序多支流程,其对switch 后的- 个表达式的值进行判断,根据值的不同,选择不同的case 分支。
switch (表达式){
case 常量表达式1:语句1;
case 常量表达式2:语句2;
...
case 常量表达式n:语句n;
default:语句n+1;
}
其语义是:计算表达式的值,并逐个与其后的常量表达式值相比较,当表达 式的值与某个常量表达式的值相等时,即执行其后的语句,然后不再进行判断, 继续执行后面所有case 后的语句。如果表达式的值与所有case 后的常量表达式均 值不相同时,则执行default 后的语句。
(4)I_whoami 函数
它是LoadRunner 提供的函数,用来得到运行时的虛拟用户的信息,包括虚拟用户D 、group值 和 场景 D D 。
我们在Action 中实现的脚本代码如下:
int Vuser,ProductNumber;
1r_whoami(avuser, NULL, NULL);//指针作为返回值传入形参。30个vuser 并发 运行此脚本时,每个vuser 会得到一个不同的vuserID,其取值为1~30 之间
ProductNumber= Vuser%10 + 1;
switch (ProductNumber)
{
case 1:
* 在实际运行的时候,每个case分支去购买不同的产品,这里我们用一条输出语句,表 明我们到达 了这里* /
1r_vuser_status_message ("I am buying producti");
break;
case2:
1r_vuser_status_message ("I am buying product2");
break;
case 3:
Ir_vuser_status_message ("I am buying product3");
break;
case4:
1r_vuser_status_message ("I am buying product4");
break;
case5:
1r_vuser_status_message ("I am buying product5");
break;
case 6:
Ir_vuser_status_message ("Iam buying product6");
break;
case 7:
1r_vuser_status_message ("I am buying product?");
break;
case 8:
Ir_vuser_status_message ( ' I am buying product8");
break;
case 9:
1r_vuser_status_message ("I am buying product9");
break;
case 10:
Ir_vuser_status_message ("I am buying product10");
break;
default:Ir_vuser_status_message ("invalid product 8d", ProductNumber);
}
这样,通过C语言的。控制逻辑,上面的脚本大大增强了灵活性,实现了Vuser 购买不同产品的分派工作
提示:default 语句一定不能丢失,否则程序在判断完所有case 分支还没有匹 配 的 话 , 就 不 知 道 该 怎 么 办 了。
理示:以上脚木代码需要在Conrooller中多用广并发运行才能粉出正确的结 果 ,因为上_whoami在VU中运行返回的VuserD总是NULL。
4. 1. 4 高级——用户自定义函数
用户可以把一些通用的功能定义成自己的函数,在脚本里调用。这对脚本库 的维护和增强很有用处。
可以通过以下几种方式來实现自定义函数。
1. 直接引用
在C 语言里,函数会增强程序的可读性和复用性,我们通常这样定义和使用 函数:
£1oat average_function(int a, int b, int c)
{//计算三个整型数的平均数,并将值返回float AverageValue=0.0; Averagevalue = (a+b+c)/3;return (AverageValue);
}
Int main()
{ float X;X = average_function (10,20,30); Printf(">>››>> The average is 8.2£", X); X= average_function (20,30,40) ;printf(">>> The average is %.2f",x) ; return 0;
}
上面的程序在main 函数里调用了另外一个average_function 函数79 average_f unction 两数用于计算三个整数的平均值,存储在一个浮点类型的变量中, 然后返回。average function 在main 阿数之前被声明和定义,因此main 能够获知 它的存在,并去调用它。
同样,LoadRuner 里用户也可以自定义函数,如果上面的例子把main 改成 Action也是可 以 的 , 如 下:
float average_function(int a, int b. int c)
{// 计算三个整型数的平均数,并将值返回float AverageValue=0.0; AverageValue = (a+b+c) /3; return (AverageValue);
}
Action ()
{float X;X= average_function (10,20,30); Ir_output_message(">二>>>> The average is 号.2f",x); x = average_function (20,30,40);1r_output me sage (">>>>>> The average is 8. 2f",x); return 0;
}
用户可以将此段代码直接拷贝到Action 中运行。
2. 本地加载模式
用于运行Vuser 脚本的C解释器使用“情性”链接模式,之所以称之“惰性”, 是因为不需要在从脚本运行开始就定义西数,我们只要在使用函数前对其进行定 义或加载即可。例如:
Lr_load_all ( " my.all” ) ;
Myfunc( ) ;/ * 在 my . a 1 1 中 定 义, 可 以 在 加 载 m y . a l 1 之 后 , 立刻 直 接 调 用 * /
Lr load dll 是LoadRunner 提供的一个加载动态链接库的两数,能够把DLL加 载进来,让你在脚本中直接使用DLL 里定义好的函数,而DLL 中的运算是编译级的,所以效率很高。
3. include 模式
在脚本里调用多个自定义函数,可以考患把这些西数写在一个文件里,在 vuser_init、Action 和vuser_end 中include 之后,就可以使用这些函数了。
4. 全局加载模式
如果我们自己开发了一个很有用的两数,比如专门从Excel 中读取测试数据, 我们想在各个类型的Vuser 脚本中都能使用这个西数,那么现在有一种更加方便 的方法:全局加载DLL 模式。
全局加载DLL,使其西数能够用于所有Vuser 脚本。DLL加载成功之后,可 以调用DLL 中定义的任何函数,而无须在脚本中对其进行声明。
要调用DLL 中定义的函数,请执行下列操作:
向mdrv.dat 文件(位于LoadRunner/dat 目录,见图4-6)的相应部分中添加要
加载的DLL 列表。 例如,要在NT平台上为WinsocketVuser加载DLL,请向mdrv.dat 文件中添
加 下列 语 句 (黑 体 语 句 ):
[winSock] ExtPriorityType-protocol
WINNT_EXT_LIBS=wsrun32.d11
WIN95_EXT_LIBS=wsrun32.d11
LINUXEXT LIBS=libIrs.so
SOLARIS_EXT_LIBS=liblrs.so
HPUX_EXT_LIBS=libIrs.sl
AIX_EXT_LIBS=1ibirs.so
LibcfgFunc=winsock_exten_conf
UtilityExt=1run_api ExtMessageQueue=0
ExtCmdLineOverwrite=-WinInet No
ExtCmdLineConc=-UsingWinInet No
WINNT_DLLS=user_dil1.dil,user_d112.dll,....
然后我们在Winsock脚本中就可以直接使用user_dll1 和user_d112 中定义的函 数 了。
同理,我们在mdrv.dat 文件的其他Vuser 配置部分中加入同样的语句,可以 达到相同的目的。
4.2 通用vU函数
通用VU 函数是构建在C 语言基础之上的脚本框架函数,起文持作用。因此学 习 通 用 V U 函 数, 有 助 于 帮 助 我 们 了解 LoadRunner 脚 本 机 制 。
通 用 V U 西 数 有 一个 特 征 , 就 是 以 L R 开 头 , 以 标 明 它 们 属 于 L R , 而 不 专 属
于任何协议,只要是C脚本,就都可使用。
通用VU 函数可分为事务控制西数、命令行分析西数、系统信息函数、字符 串西数、日志两数和运行时西数。
4.2.1 事务和事务控制函数
软件系统的性能就是靠一个个事务来度量的。LoadRunner 中事务的定义就是 靠事务函数来进行的,在执行性能测试时,LoadRunner 会采集完成其定义的每个 事务所花费的时间,并在性能测试后在Anal ysi s 中进行统计分析。
• Ir_end_sub_transaction,标记子事务的结束以便进行性能分析。
• I_end transaction,标记LoadRunner 事务的结束。
• Ir_end_transaction_instance,标记事务实例的结束以便进行性能分析。
• I_fail_trans_with_error,将打开事务的状态设置为LR_FAIL 并发送错误消
• Ir_get_trans_instance_duration,获取事务实例的持续时间 (由它的句柄指
• Ir_get_trans_instance_wasted_time,获取事务实例浪费的时间(由它的句柄 指定)。
• I r _ g e t t r a n s a c t i o n _ d u r a t i o n , 获 取 事 务 的 持 续 时 间 (按 事 务 的 名 称 ) 。
• Ir get_transaction_think_time,获取事务的思考时间 (按事务的名称)。
• Ir_get_transaction_wasted_time,获取事务浪费的时间 (按事务的名称)。
• Ir resume_transaction,继续收集事务数据以便进行性能分析。
• I resure_transaction instance,继续收集事务实例数据以便进行性能分析。
• Ir_set_transaction_instance_status,设置事务实例的状态。
• Ir_set_transaction_status,设置打开事务的状态。
• Ir set_transaction_status_by_name,设置事务的状态。
• Ir_start_sub_transaction,标记子事务的开始。
• Ir_start_transaction,标记事务的开始。
• I _start_transaction instance,启动嵌套事务 (由它的父事务的句柄指定)
• Ir_stop_transaction,停止事务数据的收集。
• I_stop_transaction_instance,停止事务(由它的句柄指定)数据的收集。
• Ir_wasted_time,消除所有打开事务浪费的时问。
4.2. 2 命令行分析函数
当LoadRunner 用命令行方式启动和运行时,以下函数用来分析命令行,以得 到命令行中的参数信息。
• ir_get_attrib double,检索脚本命令行中使用的double类型变量。
• Ir_get_attrib_long,检索脚本命令行中使用的long 类型变量。
• I get attrib string,检索脚本命令行中使用的宇符串。
4.2. 3 系统信息函数
4. 2.4用来得到 VU 的系统信息。
• I user_data point,记录用户定义的数据采集点。
• I_whoami,将有关Vuser的信息返回给Vuser脚本。
• I get host name ,返回执行 Vuser 脚本的 主机名。
• I_get master_host name,返回运行LoadRunnerController的计算机名。
字符串函数 主要是对参数进行操作,包括对不同类型参数的读取、存储和移动。
• It_eval_string,返回参数的当前值。
• I i _ s a v e _ s t r i n g , 将 以 N U L L 结 尾 的 字符 串 保 存 到 参数 中 。
• Ir_save_var,将变长宇符串保存到参数中。
• I_save_datetime,将当前日期和时间保存到参数中。
• I_advance_param,前进到下一个可用参数。
• Ir_decrypt,解密已编码的字符串。
• Ir_eval string_ext,Ir_eval_string 的扩展,为指向包含参数数据的缓冲区的
指针。
• I_eval_string_extfree,释放由Ir_eval_string_ext 分配的指针。
• Ir_save_searched_string,在缓冲区中搜索宇符串实例,并将该宇符串实例保存到参数中。
4. 2. 5 消息函数
是VU发送和记录10g 的西数。
• I_debug_message,将调试消息发送到输出窗又。
• I_error_message,将错误消息发送到输出窗又。
• It_get_debug_message,得到当前的消息类。
• 1 10g_message,将输出消息直接发送到output.txt 文件,此文件位于Vuser 脚本目录中。
• I _output_message ,将消息发送到输出窗又。
• Ir_set_debug_message,为输出消息设置消息类。
• I_vuser_status_message,生成格式化输出并将其打印到Controller Vuser 状 态区域。
• I_message,将消息发送到Vuser 日志和输出窗又。
三 注意)在runlime设量中有10g级別的设置,设置不同的级别,会决定这些西 (数在远行时是否能够生效。
4. 2. 6 运行时 ( run -time )函数
运行时(run-time)多数是通过VU的runtime 来设置的。有以下函数放在脚 本中来实现,使LoadRunner 的控制更加细致,对外更加灵敏。
• 1 load_dll,加载外部DLL。
• Ir think time,暂停脚本的执行,以模拟思考时间(实际用户在操作之间暂 停以进行思考的时间》。
• Ir_continue_on_error,指定脚本如何处理错误场景,是继续还是退出。
• Ir_rendezvous,在Vuser 脚本中设置集合点。
注:具体函数的使用方法参见LoadRunner 的帮助手册。
在 J a v a 语 言 的 脚 本 中 ,以 上 两 数 名 字 略 有 变 化 。可 参 照 L o a d R u n n e r 的 J a v a A r I 手册。
4.3 协议相关函数
除了通用 Vuser 两数以外,Vugen 还会在录制时生成特定于协议的函数,并 将它们插入到Vuser 脚本中。
特定于协议的西数是专门针对要录制的Vuser 的类型而生成的。例如,Vugen 会将LRD西数插入到数据库脚本中,将LRT函数插入到Tuxedo 脚本中,将LRS 函数插入到Windows 套接宇脚本中。
在 通 用 V U 两 数 的 基 础 上 , 不 同 协 议 类 型 的 V u s e r 有 自 己 一套 协 议 相 关 的 函 数 。 一般 地 , 协 议 相 关 的 函 数 以 本 协 议 名 字 开 头 。 如 H T T P 协 议 以 w e b 开 头 , M M S 协议以mms 开头,Database 协议以Ird 开头。下面我们以HITTP 协议为例,介绍 一 下协议相关函数。
4. 3. 1 HTTP 协议原理
HTTP 协议的Vuser 是模拟浏览器和Web Ser ver 的交互过程。
浏览器与Web Ser ver 的一 个简单的交互过程如图4-7 所示。
当浏览器想要得到一个网页时,用户在浏览器的地址栏里输入网页的URL, 然后回车,浏览器首先就会发送一个HTTP的命令到Server, Server就会把页面的 数据传送给浏览器,浏览器把数据解析成我们看到的HTML 页面。但是HTTP 协 议有一个特点,它是无状态的,也就是说,浏览器和Server 的每个交互都是独立 无关的,Server 不知道来自Bro wser 的任何两个请求是否出自同一页面,或者是否 有 次 序 的 规 定 。因 此 W e b 系 统 在 H I T T P 协 议 上 层 进 行 了 控 制 ,通 过 C o o k i e 、S e s s i o n 等机制来保证请求的状态。
4.3.2 HTTP在LoadRunner的实现
以上这个过程,完全可以由LoadRunner 的HTTP Vuser 的两数来实现。
表4-1列出了所要用到的LoadRunner 函数。
由上可见,Load Runne r 提供的这套HTTP 协议的API 是比较细致的,不仅覆 盖了浏览器与Server 的交互,而且还有设置。
以 下 是 一个 通 过 HTTP Vuser 录 制 生 成 的 脚 本 例 子 :
#include "as_web.h"
Action1 ()
{Web_add_cookie ("nav = 140, Domain = Dogbert"); Web_ur1 ("dogbert","URL = http://www.test.com", "Reccontenttype = text/html"LAST);web_image ("library","alt = library"LAST);Web_link("1 BOOK search""text = 1 book search",LAST);
}
我们逐条研究,看看用户的操作是如何转换成HI TP Vuser 两数的: 第一条语句Web_add cookie 的作用是保存Server 传过来的cookie,以后的访 问都会基于此cookie,直到脚本的结束。
第二条语句web_url 显示用户在正地址栏中输入www.test.com,然后回车,正发起 一个访问的请求。
用户进入www.test.com 页面后,点击了一个alt 属性为ibrary的图片,此操作被LoadRunner 记录为Web_image 函数。 最后用户做了一个搜索的操作,退出。
这是一个简单的Web 脚本,如果我们想生成功能强大一些的脚本,就需要了 解 V U 的 机 制 和 原 理 了。