Jmeter系列之测试计划、线程组、采样器(一)
Jmeter系列之配置元件(一)
Jmeter系列之监听器(一)
Jmeter系列之控制器(一)
Jmeter系列之控制器(二)
Jmeter系列之作用域、执行顺序
Jmeter系列之BeanShell使用BeanShell是一种完全符合Java语法规范的脚本语言,是轻量级的Java脚本,所以BeanShell和Java是可以无缝衔接的。
BeanShell的特点:
1、完整的Java语法的动态执行,Java代码片段,以及松散类型的Java和其他脚本
2、透明地访问所有Java对象和API
我对BeanShell的理解是:Java代码的脚本形式。
Java对格式的要求很严格,必须按照规定的格式编写,编译时才能通过,比如一个Java类:
public class HuMan{ // 属性 private Integer gender; // 方法 public Integer getGender() { return this.gender; } } 复制代码
那么如果我想打印Hello, World,在BeanShell和Java中该怎么写呢?
// Java[Application这个类名是随便定的] public class Application { public static void main(String[] args) { System.out.println("Hello, world"); } } 复制代码
// BeanShell // 注意这里的打印是打印到Jmeter的控制台了 System.out.println("Hello, World"); 复制代码
以上两个例子就很明显的体现了BeanShell是轻量级的Java脚本了,不用再拘泥于各种格式,同时BeanShell又能访问Java对象和API,所以在Jmeter中可以利用BeanShell来调用一些我们自己写好的Java类。
那么Jmeter中有哪些BeanShell?
1、定时器:BeanShell Timer
2、前置处理器:BeanShell PreProcessor
3、采样器:BeanShell Sampler
4、后置处理器:BeanShell PostProcessor
5、断言:BeanShell Assertion
6、监听器:BeanShell Listener
其中,每个BeanShell元件都有自己的内置对象,在元件上也有对应说明,当前元件的内置对象有哪些,比如BeanShell Sampler元件的底部有提示该元件有哪些内置对象。
这里先介绍常用的内置对象。
log | 记录日志,Jmeter使用log4j记录日志,一般使用的比较多的是log.info()、log.error(),打印的日志会记录到bin/jmeter.log文件 |
---|---|
props | 操作Jmeter属性,即jmeter.properties文件的配置 |
props.get("https.use.cached.ssl.context"): 获取对应的属性值 | |
props.put("https.use.cached.ssl.context", false): 保存数据到Jmeter属性中,如果属性不存在就创建 | |
vars | 是类JMeterVariables的对象,具体内部方法使用, 请看 操作Jmeter变量,需要注意Jmeter变量是在线程启动时,拷贝到线程的,类似线程的局部变量,所以一个线程更新了变量,不会影响到另一个线程 |
vars.get("name"): 从线程中获得变量值 | |
vars.put("key", "value"): 保存数据到线程中,如果变量不存在会创建 | |
ctx | 是类JMeterContext的对象,保存线程的上下文,该对象不是线程安全的,建议在单线程时使用,具体内部方法使用, 请看 |
prev | 是类SampleResult的对象,保存前一个请求的信息,具体内部方法使用, 请看 注意 根据上一节 作用域和执行顺序 可以知道,元件有在采样器前执行的,有在采样器后执行的,对于在采样器前执行的,prev表示的是前一个请求的信息,而对于在采样器后执行的,prev表示的当前请求的信息! |
data | 类型是byte[],即字节数组, 当前请求的响应数据,可以用String str = new String(data, "utf-8")转成字符串再打印出来 |
Reset bsh.Interpreter before each call(2):勾选后,线程每次循环前重置变量。但这个我勾选了之后,循环也没有重置变量。不知道是不是Jmeter文档没更新的原因。
Parameters(3):传递给BeanShell脚本的参数,只能传递String字符串类型。有两种方式使用:
Parameters:单个字符串,则使用Parameters;
bsh.args:数组,空白处分割,比如a b c,那么bsh.args[0]=a。
Script file(4):包含要运行的BeanShell脚本的文件,文件名存储在变量FileName中,添加了脚本文件后,就只会运行添加的脚本文件,Script部分的脚本代码不会运行了。
Script(5):要运行的BeanShell脚本,返回值如果不为null,就存储为采样器结果。
内置对象(6):
SampleResult | 是类SampleResult的对象,保存当前请求和响应的信息,具体内部方法使用, 请看 |
---|---|
ResponseCode | 响应的status code,默认为200 |
ResponseMessage | 响应信息,默认为"OK" |
IsSuccess | 请求是否成功,默认为true |
Label | 采样器的标签,即Name字段 |
FileName | 文件名,如果Script file有包含外部bsh文件 |
写到这里,我们对BeanShell Sampler也有一些了解了,那么现在就开始来写一些脚本吧,首先先明确脚本要实现什么?在初学阶段,可以从修改变量开始。
假设:现在线程有一个变量username=jack,我们需要将username修改为"Hello, jack"。
1、设置变量username=jack:
Jmeter设置变量的方式一共有4种:
1)Test Plan的User Defined Variables, 戳此回顾
2)Config Element的User Defined Variables, 戳此回顾
3)Config Element的CSV Data Set Config, 戳此回顾
4)Pre Processors的User Parameters
这里就直接使用Test Plan的User Defined Variables了,设置username=jack。
2、添加一个Sampler --> Debug Sampler,命名为before,其他配置保持默认即可,这只是用来查看变量修改前的值。
3、添加一个Sampler --> BeanShell Sampler,并先写入如下脚本:
String username = vars.get("username"); String new_username = "Hello," + username; vars.put("username", new_username); 复制代码
4、添加一个Sampler --> Debug Sampler,命名为after
5、添加一个Listener --> View Results Tree
6、运行,查看结果:
现在我们已经实现了修改变量值,现在我想把这段代码提取到一个Java的方法里,之后再在BeanShell Sampler中直接使用。
BeanShell中引用外部类,有三种方法:
1)引用源文件,即.java文件。
2)引用字节码文件,即.class文件。
3)引用jar包,即.jar文件。
现在先新建一个BeanShellMethod.java文件,并写入如下代码:
public class BeanShellMethod { public static String test(String input) { String output = "Hello," + input; return output; } } 复制代码
Jmeter提供了source函数,来引用源文件。在BeanShell Sampler中写入如下代码:
// 路径写入BeanShellMethod.java文件的绝对路径 source("/Users/Local/jmeter/scripts/BeanShellMethod.java"); String input = vars.get("username"); // BeanShellMethod.java中我们把test方法定义为static的,所以这里直接通过类名来调用就可以了 String output = BeanShellMethod.test(input); vars.put("username", output); 复制代码
运行,从after中可以看到username变量被修改了。
Jmeter提供了addClassPath函数,来引用字节码文件。首先先编译java文件,具体命令可百度搜索。
然后在BeanShell Sampler中写入如下代码:
addClassPath("/Users/zhoubihui/Local/jmeter/scripts"); import BeanShellMethod; String input = vars.get("username"); String output = BeanShellMethod.test(input); vars.put("username", output); 复制代码
运行,从after中可以看到username变量被修改了。
Jmeter引用外部jar包有两种方法:
1)将jar包拷贝到jmeter/lib/ext目录下,这种需要重启Jmeter,才能加载这个jar包;
2)Test Plan --> Add directory or jar to classpath中添加,这种不需要重启Jmeter。
首先编辑java文件,然后再将.class文件打包成jar包,再将jar包拷贝到对应目录,或者在Test Plan中添加。
然后在BeanShell Sampler中写入如下代码:
import BeanShellMethod; String input = vars.get("username"); String output = BeanShellMethod.test(input); vars.put("username", output); 复制代码
假设现在有一个登录HTTP接口,且这个登录接口对密码做了Base64加密,这个时候我们要测试这个登录接口,就可以使用BeanShell前置处理器 戳此回顾 ,先利用脚本将密码做Base64加密,HTTP采样器中再使用密码这个变量的值。
Reset bsh.Interpreter before each call(1):和BeanShell Sampler中的2一样,True表示勾选,但是我实践了发现没起作用。
内置对象(4):
sampler | 是类Sampler的对象,表示当前采样器 |
---|
BeanShell后置处理器,可以用于处理响应数据。比如现在有一个接口返回的数据里,我只需要拿到id,那么就可以在后置处理器里先处理response。或者接口返回的数据是加密的,也可以在后置处理器里先对响应数据做解密。
如果我们要认为修改响应数据,比如上面我举的例子,我只想要响应数据里的id,那么就可以通过prev这个对象来修改,prev是SampleResult的对象,查看SampleResult类的文档,可以看到一个方法:
那么我们现在就通过BeanShell PostProcessor来将接口的返回数据修改为"Hello, World",写入如下代码:
byte[] b = "Hello,World".getBytes(); prev.setResponseData(b); 复制代码
定时器会让作用域内的每一个采样器都在执行前等待一个固定的时长,如果作用域内有多个定时器,那这多个定时器就会在采样器之前全部执行,所以多个定时器的延迟时长是每个定时器的延时时长的累加。
戳此回顾 作用域和执行顺序。
现在,我们想要一个采样器延迟2秒再执行,写入如下代码:
// 单位是毫秒 Thread.sleep(2000); 复制代码
从日志文件bin/jmeter.log中可以看到采样器延迟了2秒。
可以看到BeanShell断言的内置对象很多,可读/可写的意思是我们可以更改,比如说SampleResult是当前请求响应的数据,说白了,在BeanShell断言里同样可以修改响应数据,和BeanShell后置处理器一样。
Failure | boolean,用来设置断言状态,为true,表明断言失败 |
---|---|
FailureMessage | String,用来设置断言信息 |
SampleResult | SampleResult,具体内部方法, 请看 |
Response | SampleResult,具体内部方法, 请看 |
ResponseData | byte[],响应数据 |
ResponseCode | String,status code的值,比如200,404 |
ResponseMessage | String,响应信息,比如OK |
ResponseHeaders | String,响应头 |
RequestHeaders | String,请求头 |
SampleLabel | String,采样器的Name |
SamplerData | String,就是截图中的内容 |
那么,我们现在先和BeanShell后置处理器中一样,只把响应修改为"Hello, World",写入如下代码:
byte[] b = "Hello, World".getBytes(); SampleResult.setResponseData(b); 复制代码
可以看到BeanShell断言同样可以实现修改响应数据,不过修改响应数据还是放在BeanShell后置处理器中比较好,不同的元件负责不同的功能,断言就只做断言判断。
现在我们用ResponseCode来判断,等于200时,设置为断言失败,写入如下代码:
if ("200".equals(ResponseCode)) { Failure = true; FailureMessage = "心情不好"; } 复制代码
断言失败的,在查看结果树会标红显示:
内置对象: