浅谈重要组件Beanshell

浅谈重要组件Beanshell

BeanShell是JMeter中一个非常实用的组件,它允许用户通过Java-like的脚本语言执行自定义逻辑,从而极大地扩展了JMeter的功能。本文将详细介绍BeanShell的使用方法、常见用途、以及其在JMeter中的具体应用场景。
BeanShell概述
BeanShell是一种轻量级的Java脚本解释器,它支持即席(adhoc)的脚本编写和快速原型开发。在JMeter中,BeanShell可以用于创建动态且高度定制化的测试计划组件,如采样器、前置处理器、后置处理器、断言、计时器和监听器。相比于JMeter的图形界面操作,BeanShell提供了更高的灵活性和编程控制能力。

BeanShell的组件及用途

  1. BeanShell Sampler(取样器)
    ● 用途:执行自定义的Java代码片段,可以用来发送请求、处理响应或执行任何复杂的业务逻辑。
    ● 示例:模拟复杂请求、动态生成请求参数。
  2. BeanShell PreProcessor(前置处理器)
    ● 用途:在主采样器执行前运行,常用于数据预处理,如修改请求参数、设置变量等。
    ● 示例:根据某些条件动态计算变量值。
  3. BeanShell PostProcessor(后置处理器)
    ● 用途:在采样器执行后运行,用于解析响应数据,提取有用信息存储为变量。
    ● 示例:从HTML或JSON响应中提取数据。
  4. BeanShell Assertion(断言)
    ● 用途:提供更灵活的验证机制,通过脚本判断响应是否符合预期。
    ● 示例:对比响应数据与预期值的复杂逻辑验证。
  5. BeanShell Timer(定时器)
    ● 用途:基于条件控制请求间的延迟时间,提供更灵活的定时控制。
    ● 示例:根据服务器响应时间动态调整下一个请求的等待时间。
  6. BeanShell Listener(监听器)
    ● 用途:监听并处理测试结果,可用于日志记录、数据分析等。
    ● 示例:收集测试期间的特定数据,输出至文件或控制台。

应用实例

在BeanShell组件中,以下是一些常用的内置变量,它们可以帮助你访问JMeter上下文中的信息,首先使用SpringBoot编写一个接口,部分代码如下

 @PostMapping(value = "/login",produces = "application/json;charset=UTF-8")
    public String authenticate(@RequestBody JSONObject request) {

        String validUsername = "admin";
        String validPassword = "password";

        if (request.getString("username").equals(validUsername) && request.getString("password").equals(validPassword)) {
            return "认证成功";
        } else {
            return "认证失败";
        }
    }

log关键字

作用:打印日志信息,如 log.info(“This is an info message.”);
编写如下脚本:
在这里插入图片描述
线程组:保持默认
BeanShell 取样器:编写如下脚本

log.info("This is an info message.");
log.warn("This is an warn message.");
log.error("This is an error message.");

运行脚本,查看日志信息:

2024-07-08 16:05:21,685 INFO o.a.j.u.BeanShellTestElement: This is an info message.
2024-07-08 16:05:21,685 WARN o.a.j.u.BeanShellTestElement: This is an warn message.
2024-07-08 16:05:21,685 ERROR o.a.j.u.BeanShellTestElement: This is an error message.

var关键字

作用:操作JMeter局部变量,例如设置变量 vars.put(“myVar”, “value”); 或获取变量 vars.get(“myVar”);
编写如下脚本:
在这里插入图片描述
线程组:保持默认
BeanShell 取样器1:编写如下脚本

String s = "test";
vars.put("val",s);
String value = vars.get("val");
log.info("value变量的值为:" + value);

BeanShell 取样器2:编写如下脚本

log.info("val变量的值为:${val}");

运行脚本,查看日志信息:
2024-07-08 16:31:28,626 INFO o.a.j.u.BeanShellTestElement: value变量的值为:test
2024-07-08 16:31:28,641 INFO o.a.j.u.BeanShellTestElement: val变量的值为:test

props关键字

作用:操作JMeter全局属性,如设置属性 props.put(“PROP_NAME”, “value”); 或读取属性 props.get(“PROP_NAME”);
测试脚本沿用log关键字的脚本,并且将BeanShell 取样器中的内容代码替换如下

props.put("PROP_NAME", "value");
String value = props.get("PROP_NAME");
log.info("PROP_NAME变量的值为:" + value);

运行脚本,查看日志信息:

2024-07-08 16:33:25,939 INFO o.a.j.u.BeanShellTestElement: PROP_NAME变量的值为:value

prev关键字

作用:访问前一个采样器的响应数据等,如获取响应文本 prev.getResponseDataAsString();
编写脚本如下:
在这里插入图片描述
线程组:保持默认
HTTP信息头管理器:添加信息头,名称:content-type,值:application/json;charset=UTF-8
HTTP请求:协议http,服务器名称或IP为127.0.0.1,端口号为8091,请求方法post,路径/login,内容编号utf-8,消息体数据如下:

{
    "username": "admin",
    "password": "password"
    }

BeanShell 后置处理程序:编写如下代码

log.info("获得响应信息:");
log.info(prev.getResponseMessage());
log.info("获得响应文本内容:");
log.info(prev.getResponseDataAsString());
log.info("获得线程名称:");
log.info(prev.getThreadName());
log.info("获得取样器标签:");
log.info(prev.getSampleLabel());
log.info(prev.getSampleLabel(true));//含线程组
log.info("获得线程取样时间:");
log.info(prev.getTime().toString());
log.info("获得编码方式:");
log.info(prev.getDataEncodingWithDefault());
log.info("设置编码方式:");
prev.setDataEncoding("gbk");
log.info(prev.getDataEncodingWithDefault());
log.info("获得消息头:");
log.info(prev.getResponseHeaders());
log.info("获得Content-Type字段:");
log.info(prev.getContentType());
log.info("获取请求开始时间戳和结束时间戳:");
log.info("开始时间:" + prev.getStartTime());
log.info("结束时间:" + prev.getEndTime());
log.info("获得响应状态码:");
log.info(prev.getResponseCode());
log.info("设置响应状态码为指定值:");
prev.setResponseCode("401");
log.info(prev.getResponseCode());
log.info("设置响应状态码变成200,及时返回的并不是200:");
prev.setResponseCodeOK();
log.info(prev.getResponseCode());
log.info("判断返回值是否是200,如果是则返回true,如果不是则是返回false:");
log.info(prev.isResponseCodeOK().toString());
log.info("获取URL的信息:");
log.info(prev.getUrlAsString());

运行脚本,查看日志信息:

2024-07-08 17:00:18,137 INFO o.a.j.t.JMeterThread: Thread started: 线程组 1-1
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 获得响应信息:
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement:
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 获得响应文本内容:
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 认证成功
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 获得线程名称:
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 线程组 1-1
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 获得取样器标签:
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: HTTP请求
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 线程组:HTTP请求
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 获得线程取样时间:
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 5
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 获得编码方式:
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: UTF-8
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 设置编码方式:
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: gbk
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 获得消息头:
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Content-Length: 12
Date: Mon, 08 Jul 2024 09:00:18 GMT
Keep-Alive: timeout=60
Connection: keep-alive
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 获得Content-Type字段:
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: application/json;charset=UTF-8
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 获取请求开始时间戳和结束时间戳:
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 开始时间:1720429218145
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 结束时间:1720429218150
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 获得响应状态码:
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 200
2024-07-08 17:00:18,153 INFO o.a.j.u.BeanShellTestElement: 设置响应状态码为指定值:
2024-07-08 17:00:18,168 INFO o.a.j.u.BeanShellTestElement: 401
2024-07-08 17:00:18,168 INFO o.a.j.u.BeanShellTestElement: 设置响应状态码变成200,及时返回的并不是200:
2024-07-08 17:00:18,168 INFO o.a.j.u.BeanShellTestElement: 200
2024-07-08 17:00:18,168 INFO o.a.j.u.BeanShellTestElement: 判断返回值是否是200,如果是则返回true,如果不是则是返回false:
2024-07-08 17:00:18,168 INFO o.a.j.u.BeanShellTestElement: true
2024-07-08 17:00:18,168 INFO o.a.j.u.BeanShellTestElement: 获取URL的信息:
2024-07-08 17:00:18,168 INFO o.a.j.u.BeanShellTestElement: http://127.0.0.1:8091/login
2024-07-08 17:00:18,168 INFO o.a.j.t.JMeterThread: Thread is done: 线程组 1-1

ctx关键字

作用: 访问JMeter上下文(Context),进行更高级别的操作。
我们沿用prev中的脚本结构,其他不变,只是变更BeanShell 后置处理程序内容
BeanShell 后置处理:编写如下代码

String s = "test";
vars.put("val",s);
log.info("提供对当前线程的变量的访问:");
log.info(ctx.getVariables().get("val"));
log.info("获取相关属性:");
log.info(ctx.getVariables().get("START.YMD"));
log.info("获取取样器结果:");
log.info(ctx.getPreviousResult().toString());
log.info("获取当前取样器内容:");
log.info(ctx.getCurrentSampler().toString());
//这个大家可以自己试试
//log.info("获取前一个取样器内容:");
//log.info(ctx.getPreviousSampler().getName());
log.info("获取当前线程号:");
log.info(ctx.getThreadNum().toString());
log.info("获取线程内容:");
log.info(ctx.getThread().toString());
log.info("获取当前线程组:");
log.info(ctx.getThreadGroup().toString());

运行脚本,查看日志信息:

2024-07-09 10:30:08,580 INFO o.a.j.t.JMeterThread: Thread started: 线程组 1-1
2024-07-09 10:30:08,599 INFO o.a.j.u.BeanShellTestElement: 提供对当前线程的变量的访问:
2024-07-09 10:30:08,600 INFO o.a.j.u.BeanShellTestElement: test
2024-07-09 10:30:08,616 INFO o.a.j.u.BeanShellTestElement: 获取相关属性:
2024-07-09 10:30:08,617 INFO o.a.j.u.BeanShellTestElement: 20240705
2024-07-09 10:30:08,620 INFO o.a.j.u.BeanShellTestElement: 获取取样器结果:
2024-07-09 10:30:08,625 INFO o.a.j.u.BeanShellTestElement: HTTP请求
2024-07-09 10:30:08,628 INFO o.a.j.u.BeanShellTestElement: 获取当前取样器内容:
2024-07-09 10:30:08,631 INFO o.a.j.u.BeanShellTestElement: http://127.0.0.1:8091/login
Query Data:
2024-07-09 10:30:08,632 INFO o.a.j.u.BeanShellTestElement: 获取当前线程号:
2024-07-09 10:30:08,636 INFO o.a.j.u.BeanShellTestElement: 0
2024-07-09 10:30:08,639 INFO o.a.j.u.BeanShellTestElement: 获取线程内容:
2024-07-09 10:30:08,640 INFO o.a.j.u.BeanShellTestElement: org.apache.jmeter.threads.JMeterThread@41d9ee4c
2024-07-09 10:30:08,644 INFO o.a.j.u.BeanShellTestElement: 获取当前线程组:
2024-07-09 10:30:08,645 INFO o.a.j.u.BeanShellTestElement: org.apache.jmeter.threads.ThreadGroup@54aac53d
2024-07-09 10:30:08,645 INFO o.a.j.t.JMeterThread: Thread is done: 线程组 1-1

SamplerData关键字
作用:获取请求的数据
编写脚本如下:
在这里插入图片描述

线程组:保持默认
HTTP信息头管理器:添加信息头,名称:content-type,值:application/json;charset=UTF-8
HTTP请求:协议http,服务器名称或IP为127.0.0.1,端口号为8091,请求方法post,路径/login,内容编号utf-8,消息体数据如下:

{
    "username": "admin",
    "password": "password"
    }

BeanShell断言:编写如下代码

log.info("获取请求数据:");
String reuest = new String(SamplerData);
log.info(reuest);

运行脚本,查看日志信息:

2024-07-09 10:36:54,071 INFO o.a.j.t.JMeterThread: Thread started: 线程组 1-1
2024-07-09 10:36:54,080 INFO o.a.j.u.BeanShellTestElement: 获取请求数据:
2024-07-09 10:36:54,081 INFO o.a.j.u.BeanShellTestElement: POST http://127.0.0.1:8091/login
POST data:
{
“username”: “admin”,
“password”: “password”
}
[no cookies]
2024-07-09 10:36:54,082 INFO o.a.j.t.JMeterThread: Thread is done: 线程组 1-1

Label关键字

作用:获取本组件名称
我们沿用SamplerData中的脚本结构,其他不变,只是变更BeanShell断言程序内容
BeanShell断言:编写如下代码

log.info("获取组件名称:");
log.info(Label);

运行脚本,查看日志信息:

2024-07-09 10:45:25,313 INFO o.a.j.t.JMeterThread: Thread started: 线程组 1-1
2024-07-09 10:45:25,323 INFO o.a.j.u.BeanShellTestElement: 获取组件名称:
2024-07-09 10:45:25,323 INFO o.a.j.u.BeanShellTestElement: BeanShell断言
2024-07-09 10:45:25,323 INFO o.a.j.t.JMeterThread: Thread is done: 线程组 1-1

SampleLabel关键字

作用:获取父取样器名称
我们沿用SamplerData中的脚本结构,其他不变,只是变更BeanShell断言程序内容
BeanShell断言:编写如下代码

log.info("获取组件名称:");
log.info(SampleLabel);

运行脚本,查看日志信息:

2024-07-09 10:47:51,087 INFO o.a.j.t.JMeterThread: Thread started: 线程组 1-1
2024-07-09 10:47:51,094 INFO o.a.j.u.BeanShellTestElement: 获取父取样器名称:
2024-07-09 10:47:51,094 INFO o.a.j.u.BeanShellTestElement: HTTP请求
2024-07-09 10:47:51,094 INFO o.a.j.t.JMeterThread: Thread is done: 线程组 1-1

ResponseData关键字

作用:获取响应数据
我们沿用SamplerData中的脚本结构,其他不变,只是变更BeanShell断言程序内容
BeanShell断言:编写如下代码

log.info("获取响应数据:");
String response = new String(ResponseData,"utf-8");
log.info(response);

运行脚本,查看日志信息:

2024-07-09 10:49:26,066 INFO o.a.j.t.JMeterThread: Thread started: 线程组 1-1
2024-07-09 10:49:26,074 INFO o.a.j.u.BeanShellTestElement: 获取响应数据:
2024-07-09 10:49:26,076 INFO o.a.j.u.BeanShellTestElement: 认证成功
2024-07-09 10:49:26,086 INFO o.a.j.t.JMeterThread: Thread is done: 线程组 1-1

ResponseCode关键字

作用:获取响应码
我们沿用SamplerData中的脚本结构,其他不变,只是变更BeanShell断言程序内容
BeanShell断言:编写如下代码

log.info("获取响应码:");
log.info(ResponseCode);

运行脚本,查看日志信息:

2024-07-09 10:51:15,850 INFO o.a.j.t.JMeterThread: Thread started: 线程组 1-1
2024-07-09 10:51:15,896 INFO o.a.j.u.BeanShellTestElement: 获取响应码:
2024-07-09 10:51:15,902 INFO o.a.j.u.BeanShellTestElement: 200
2024-07-09 10:51:15,903 INFO o.a.j.t.JMeterThread: Thread is done: 线程组 1-1

ResponseMessage关键字

作用:获取响应信息
我们沿用SamplerData中的脚本结构,其他不变,只是变更BeanShell断言程序内容
BeanShell断言:编写如下代码

log.info("获取响应码:");
log.info(ResponseMessage);

运行脚本,查看日志信息:

2024-07-09 10:51:54,218 INFO o.a.j.t.JMeterThread: Thread started: 线程组 1-1
2024-07-09 10:51:54,225 INFO o.a.j.u.BeanShellTestElement: 获取响应码:
2024-07-09 10:51:54,225 INFO o.a.j.u.BeanShellTestElement:
2024-07-09 10:51:54,227 INFO o.a.j.t.JMeterThread: Thread is done: 线程组 1-1

我们可以看到上述内容打印是空,主要是在查看结果树中我们可以看到HTTP请求的取样器结果中的Response message也为空

Thread Name: 线程组 1-1
Sample Start: 2024-07-09 10:51:54 CST
Load time: 3
Connect Time: 1
Latency: 3
Size in bytes: 180
Sent bytes:243
Headers size in bytes: 168
Body size in bytes: 12
Sample Count: 1
Error Count: 0
Data type (“text”|“bin”|“”): text
Response code: 200
Response message:
HTTPSampleResult fields:
ContentType: application/json;charset=UTF-8
DataEncoding: UTF-8

Failure和FailureMessage关键字:

作用:设置断言失败或者成功,并且打印错误信息
我们沿用SamplerData中的脚本结构,其他不变,只是变更BeanShell断言程序内容

BeanShell断言:编写如下代码
log.info("设置断言失败:");
Failure = true;
log.info("设置断言失败信息:");
FailureMessage = "断言失败";

运行脚本,查看结果树:
在这里插入图片描述
查看HTTP请求下面BeanShell断言的Assertion result,结果内容如下

Assertion error: false
Assertion failure: true
Assertion failure message: 断言失败

Response关键字:

作用:响应相关信息内容获取
我们沿用SamplerData中的脚本结构,其他不变,只是变更BeanShell断言程序内容
BeanShell断言:编写如下代码

log.info("获取响应消息信息:");
log.info(Response.getResponseDataAsString());
log.info("获取响应对应线程名称:");
log.info(Response.getThreadName());
log.info("获取响应码:");
log.info(Response.getResponseCode());

运行脚本,查看日志信息

2024-07-09 11:11:21,241 INFO o.a.j.t.JMeterThread: Thread started: 线程组 1-1
2024-07-09 11:11:21,251 INFO o.a.j.u.BeanShellTestElement: 获取响应消息信息:
2024-07-09 11:11:21,261 INFO o.a.j.u.BeanShellTestElement: 认证成功
2024-07-09 11:11:21,261 INFO o.a.j.u.BeanShellTestElement: 获取响应对应线程名称:
2024-07-09 11:11:21,261 INFO o.a.j.u.BeanShellTestElement: 线程组 1-1
2024-07-09 11:11:21,261 INFO o.a.j.u.BeanShellTestElement: 获取响应码:
2024-07-09 11:11:21,261 INFO o.a.j.u.BeanShellTestElement: 200
2024-07-09 11:11:21,261 INFO o.a.j.t.JMeterThread: Thread is done: 线程组 1-1

IsSuccess关键字

作用:设置取样器成功还是失败
我们编写如下脚本
在这里插入图片描述
线程组:保持默认
HTTP信息头管理器:添加信息头,名称:content-type,值:application/json;charset=UTF-8
HTTP请求:协议http,服务器名称或IP为127.0.0.1,端口号为8091,请求方法post,路径/login,内容编号utf-8,消息体数据如下:

{
    "username": "admin",
    "password": "password"
    }

BeanShell 取样器:编写如下代码

log.info("设置取样器成功还是失败:");
IsSuccess = false;

运行脚本,结果树中查看BeanShell 取样器结果:
在这里插入图片描述当BeanShell 取样器:编写如下代码

log.info("设置取样器成功还是失败:");
IsSuccess = true;

运行脚本,结果树中查看BeanShell 取样器结果:
在这里插入图片描述

BeanShell的最佳实践

● 优化性能:尽管BeanShell提供了灵活性,但其执行速度通常慢于Java代码。对于性能关键的场景,考虑使用JSR223 Samplers(尤其是Groovy),因为Groovy拥有更快的执行效率。
● 代码简洁性:保持脚本简洁、可读性强,使用注释来提高可维护性。
● 异常处理:合理使用try-catch语句来捕获并处理异常,避免脚本因未预料的错误中断。
● 安全考量:避免在脚本中硬编码敏感信息,使用JMeter的属性或变量管理敏感数据。

总结

BeanShell为JMeter用户提供了一种强大的手段来定制和扩展测试计划,使得应对复杂测试需求成为可能。通过熟练掌握BeanShell的使用,测试工程师能够设计出更加贴近实际应用场景的测试案例,从而提高测试的准确性和效率。然而,在追求定制化的同时,也应关注脚本的性能和安全性,确保测试的有效性和可靠性。

JMeter中的BeanShell后置处理器是一种强大的工具,用于在HTTP请求之后自动执行Java代码。它在请求之后执行,并且可以访问请求的响应数据,以及其他JMeter变量和属性。BeanShell后置处理器可以用于许多用例,例如解析响应数据并提取所需的值,验证响应数据是否符合预期,并根据结果执行其他操作等。 以下是使用BeanShell后置处理器的步骤: 1. 添加BeanShell后置处理器到HTTP请求中。 2. 在BeanShell后置处理器中编写Java代码来处理响应数据。 3. 通过JMeter变量或属性将处理后的数据发送到其他请求中。 4. 运行测试计划并查看结果。 下面是一个示例BeanShell后置处理器代码,该代码从响应中提取特定值并将其存储在JMeter变量中: ``` import org.apache.jmeter.extractor.*; import org.apache.jmeter.extractor.gui.*; String responseData = prev.getResponseDataAsString(); String extractedValue = responseData.substring(10, 20); vars.put("myVariable", extractedValue); ``` 该代码首先将响应数据作为字符串获取,然后从中提取10到20个字符的子字符串。最后,它将提取的值存储在名为“myVariable”的JMeter变量中,以便在后续请求中使用。 需要注意的是,BeanShell后置处理器的性能可能会受到影响,因为它会在每个HTTP请求之后执行Java代码。对于大型测试计划,这可能会导致性能瓶颈。因此,在使用BeanShell后置处理器时,请确保仅编写必要的代码,并尽可能优化代码以提高性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔波儿灞爱霸波尔奔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值