JMeter BeanShell Sampler与JMeter BeanShell断言

JMeter BeanShell Sampler与JMeter BeanShell断言BeanShell 是一种完全符合 Java 语法规范的脚本语言 当然 也有一些不一样的地方 比如 BeanShell 就不支持 Java 中的泛型 但又拥有一些独有的语法和方法 BeanShell 应用在 JMeter 的三个元件中 BeanShellSam 位于 Sampler 中 BeanShellPre 位于前置处理器 和 BeanShellPos 位于后置

前言

BeanShell是一种完全符合Java语法规范的脚本语言,但又拥有一些独有的语法和方法。(当然,也有一些不一样的地方,比如 BeanShell就不支持Java中的泛型,当初也是一个深坑)。

BeanShell应用在JMeter的四个组件中:

  1. BeanShell Sampler:BeanShell取样器,位于 Sampler(取样器)中,与常用的【HTTP请求】取样器性质类似,也是一个独立的取样器,会被【聚合报告】所统计。
  2. BeanShell PreProcessor:BeanShell前置处理器,位于【前置处理器】中,作用于一个取样器上,且在取样器执行前执行,一般用于对取样器入参进行处理。
  3. BeanShell PostProcessor:BeanShell前置处理器,位于【后置处理器】中,作用于一个取样器上,且在该取样器执行后执行,一般用于对取样器结果进行处理。
  4. Beanshell Assertion:Beanshell 断言,位于【断言】中,作用于取样器上,且在该取样器执行后执行,用于对取样器响应结果进行断言。

介绍几个BeanShell常用的独有方法:

序号 名称 作用
1 vars.get(“variableName”) 根据变量名获取内存中的变量值,前提是这个值在脚本前文中已经定义并赋值
2 vars.put(“variableName”,“variableValue”) 将一个值存储到变量中,脚本下文中可以通过${variableName}引用
3 prev.getResponseDataAsString() 获取sampler(取样器)的响应数据并以String类型接收,用在【后置处理器】的【BeanShell PostProcessor中】
更多内置方法见:JMeter API文档

关于JMeter的使用,我花费一些精力写了JMeter的一系列文章,有图有案例,一方面总结起来作为备忘,一方面希望能给初学者一些帮助。觉得有所帮助的朋友,请点个赞,对于疏漏之处也欢迎指教。

  • JMeter逻辑控制器:https://blog.csdn.net/mu_wind/article/details/
  • JMeter配置元件:https://blog.csdn.net/mu_wind/article/details/
  • JMeter操作Mysql数据库: https://blog.csdn.net/mu_wind/article/details/
  • JMeter关联:正则表达式提取器与JSON提取器:https://blog.csdn.net/mu_wind/article/details/
  • JMeter Linux下执行测试:https://blog.csdn.net/mu_wind/article/details/
  • JMeter自定义日志与日志分析:https://blog.csdn.net/mu_wind/article/details/

1 BeanShell操作变量

前面说到了BeanShell的独有方法,vars.getvars.put了,BeanShell对变量的操作主要就是依赖这两个方法。

首先创建一个脚本,【用户定义的变量】中定义了一个变量,变量名为paramIn,值为Mu
在这里插入图片描述
【BeanShell Sampler】中写入下面语句:
在这里插入图片描述
HTTP请求对【BeanShell Sampler】中put出的变量进行引用:
在这里插入图片描述
运行脚本,查看结果树,可以看到变量引用成功:在这里插入图片描述
在这里插入图片描述
在BeanShell中直接写代码,方便快捷,在代码量不大的情况下十分便利。如果出于规范化考虑,尤其代码量较大时,为了使BeanShell看起来更清晰,可以按下面的方式写,效果是一样的:
在这里插入图片描述


















// 定义一个方法 public void test(){ 
    // vars.get 获取paramIn的值 String paramIn = vars.get("paramIn"); // 一个简单的字符串拼接 String string = "Hello," + paramIn; // vars.put()生成一个变量且赋值 vars.put("param",string); vars.put("paramOut",string + ",Welcome"); } // 需要主动调用函数,否则函数不会自动起作用 test(); 

2 BeanShell引用外部资源

如果JMeter脚本的代码量比较小,那么直接在将代码写在Beanshell中就可以了。如果代码量比较大,在Beanshell里写起来就比较困难,这时候可以考虑引用外部资源,包括引用.java文件、.class文件、.jar文件三种方式。

首先,我们写好这么一个类,类内的md5Encryption方法,是将一个字符串转化为一个经过MD5加密过的新字符串。

import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class Md5Encryption { 
    public static String md5Encryption(String string) { 
    try { 
    MessageDigest md = MessageDigest.getInstance("MD5"); md.update(string.getBytes()); byte[] b = md.digest(); int i; StringBuffer buf = new StringBuffer(""); for (int offset = 0; offset < b.length; offset++) { 
    i = b[offset]; if (i < 0) i += 256; if (i < 16) buf.append("0"); buf.append(Integer.toHexString(i)); } return buf.toString(); } catch (NoSuchAlgorithmException e) { 
    e.printStackTrace(); return null; } } } 

2.1 引用java文件

上面的代码在Md5Encryption.java文件中,JMeter支持直接引用java文件。
在这里插入图片描述

// 引用外部.java文件,注意路径中要使用"/" source("D:/Md5Encryption.java"); String passwordIni = ""; // 调用加密方法 String passwordEncryp = new Md5Encryption().md5Encryption(passwordIni); vars.put("passwordEncryp",passwordEncryp); 

2.2 addClassPath

如果一个java文件不满足需求,那么可以把引用范围扩大到整个项目,如下,整个mutest项目src目录下的所有类都可以通过 import 方式引用:
在这里插入图片描述

// 添加路径:类所在项目的目录 addClassPath("E:/project/workspace/mutest/src"); // 引入 import mutest.Md5Encryption; String passwordIni = ""; // 调用加密方法 String passwordEncryp = new Md5Encryption().md5Encryption(passwordIni); vars.put("passwordEncryp",passwordEncryp); 

2.3 引用jar包

前面两种方式呢,受外部影响太大,比如Md5Encryption被修改了,或者路径发生变化了,JMeter脚本都会受影响。为了规避这种影响,我们可以将项目打成jar包,导入JMeter安装目录\lib\etc中,并重启JMeter

// import时带上类的包名 import mutest.Md5Encryption; String passwordIni = ""; // 调用加密方法 String passwordEncryp = new Md5Encryption().md5Encryption(passwordIni); vars.put("passwordEncryp",passwordEncryp); 

3 BeanShell断言

接口测试中,所谓断言,是指用一定的判断规则对接口响应数据进行校验,不匹配则判定接口测试失败。在JMeter中,不加断言的话,默认校验接口的响应码。

3.1 校验JSONObject

首先使用【BeanShell Sampler】作为mock server返回这样的预期结果:

{ 
    "code" : 0, "goodsInfo" : { 
    "name" : "computer", "price" : 4500, "size" : 60 } } 
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; // prev.getResponseDataAsString()方法获取接口响应数据 String goodsDataRes = prev.getResponseDataAsString(); // 将String解析成JSONObject并获取goodsInfo JSONObject goodsInfoRes = JSON.parseObject(goodsDataRes).getJSONObject("goodsInfo"); // 定义一个新的JSONObject用来存储数据库数据 JSONObject goodsInfoDb = new JSONObject(); try{ 
    goodsInfoDb.put("name",vars.get("name_1")); // 这里注意将数字转为int类型 goodsInfoDb.put("price",Integer.valueOf(vars.get("price_1"))); goodsInfoDb.put("size",Integer.valueOf(vars.get("size_1"))); if(!goodsInfoRes.equals(goodsInfoDb)){ 
    Failure = true; String message = "接口返回数据与数据库数据不一致!\n"; FailureMessage = message + "数据库内容: " + goodsInfoDb + "\n响应内容: " + goodsInfoRes; } }catch(Exception e){ 
    Failure = true; String message = "数据库数据为空!\n"; FailureMessage = message + "数据库内容: \n" + goodsInfoDb + "\n" + "响应内容: \n" + goodsInfoRes; } 

3.2 校验含JSONArray的JSON

我们将问题复杂化一些,假如接口返回的是包含JSONArray的数据,如下:

{ 
    "code" : 0, "data" : [{ 
    "name" : "iphone", "price" : 6000, "size" : 55 }, { 
    "name" : "watch", "price" : 500, "size" : 35 },{ 
    "name" : "computer", "price" : 4500, "size" : 60 } ] } 
return "{\"code\" : 0,\"data\" : [{\"name\" : \"iphone\",\"price\" : 6000,\"size\" : 55}, {\"name\" : \"watch\",\"price\" : 500,\"size\" : 35},{\"name\" : \"computer\",\"price\" : 4500,\"size\" : 60}]}"; 

要解决顺序问题,我想到的方案是对数据进行进一步加工,将JSONArray处理成JSONObject格式,这样就消除了顺序的影响(不含JSONArray的JSONObject的对比是不受元素顺序影响的),【BeanShell断言】内代码贴上:

import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONArray; String goodsDataRes = prev.getResponseDataAsString(); JSONArray goodsListRes = JSON.parseObject(goodsDataRes).getJSONArray("data"); vars.put("goodsListRes",goodsListRes.toString()); JSONObject goodsInfoDb = new JSONObject(); JSONObject goodsInfoRes = new JSONObject(); try{ 
    for(int i = 1;i <= Integer.parseInt(vars.get("name_#"));i++){ 
    JSONObject goods = new JSONObject(); goods.put("name",vars.get("name_" + i)); goods.put("price",Integer.parseInt(vars.get("price_" + i))); goods.put("size",Integer.parseInt(vars.get("size_" + i))); goodsInfoDb.put(vars.get("name_" + i),goods); } for(int i = 0; i < goodsListRes.size();i++){ 
    JSONObject goods = goodsListRes.getJSONObject(i); goodsInfoRes.put(goods.getString("name"), goods); } if(goodsInfoRes.size() != goodsInfoDb.size()){ 
    Failure = true; String message = "接口返回数据与数据库数据的数量不一致!\n"; FailureMessage = message + "数据库数据数量: " + goodsInfoDb.size() + "\n响应数据数量: " + goodsInfoRes.size() + "\n数据库内容: " + goodsInfoDb + "\n响应内容: " + goodsInfoRes; }else if(!goodsInfoRes.equals(goodsInfoDb)){ 
    Failure = true; String message = "接口返回数据与数据库数据的内容不一致!\n"; FailureMessage = message + "数据库内容: " + goodsInfoDb + "\n响应内容: " + goodsInfoRes; } }catch(Exception e){ 
    Failure = true; String message = "数据处理异常!\n"; FailureMessage = message + "数据库内容: \n" + goodsInfoDb + "\n" + "响应内容: \n" + goodsInfoRes; } 

使用这个断言,我们测试一下:

  1. 数据处理异常,可以人为将sql写错,例如:select price,size from test.goods where status=1(缺少name字段);
    在这里插入图片描述

  2. 接口数据与数据库数据的数量不一致,可以将数据库数据篡改一下:
    在这里插入图片描述
    在这里插入图片描述




  3. 数据库数据和接口响应数据数量一致,但内容不同
    在这里插入图片描述
    在这里插入图片描述




  4. 数据库数据与接口响应数据一致,断言通过。
    在这里插入图片描述
    以上,是BeanShell的相关知识,后续还会更新更多的使用场景。




版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/201200.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月20日 上午9:51
下一篇 2026年3月20日 上午9:52


相关推荐

  • Rootkit演变

    Rootkit演变Rootkit 概述 nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp 我第一次接触 rootkit 是在 2004 年 当时我还是一个 rookie 病毒分析师 具备一定的关于 UNIX 的 rootkit 病毒的相关知识 有一天我无意中发现 windows 系统中的一个可执行程序 在我登录这个程序的时候 windows 系统似乎没有做出任何反应 我觉得很有趣 并且准备进一步探究这个程序 然后我发现在载入模块列表中有个文件并没有出现在磁盘上 很显然

    2025年12月6日
    5
  • DropDownList的常用属性和事件「建议收藏」

    DropDownList的常用属性和事件「建议收藏」SelectedItem属性设置或获取下拉菜单的选中项,该属性的类型为System.Web.UI.WebControls.ListItem.所有列表控件(ListControl)中的项都是该类型,它

    2022年7月3日
    58
  • AspNetPager组件

    AspNetPager组件UrlPaging=”false”NumericButtonTextFormatString=”[{0}]”CustomInfoHTML=”第%CurrentPageIndex%页共%PageCount%页显示%StartRecordIndex%-%EndRecordIndex%条”                                   ShowCusto

    2025年7月31日
    8
  • gg修改器修改数值没有用怎么办_gg修改器怎么用怎么修改数值 修改数值方法介绍…[通俗易懂]

    gg修改器修改数值没有用怎么办_gg修改器怎么用怎么修改数值 修改数值方法介绍…[通俗易懂]gg修改器怎么用怎么修改数值修改数值方法介绍GG修改器-全称GameGuardian是非常好用的手机修改器,但它需要ROOT权限,而现在要想ROOT一台手机难度是很大的,因此,本文介绍最新的GG修改免ROOT框架使用方法。现在市面上很多多开框架都支持ROOT,但支持最新安卓Q或者安卓11的却很少,并且运行GG修改器时会经常报错。并且,很多用户发现GG修改器也很难下载。X8沙箱,据说拥有完整系统级别…

    2025年9月13日
    5
  • 掩码、通配符与反掩码

    掩码、通配符与反掩码掩码采用按位与运算,计算一个ip地址的网络号0&0=0  1&0=0  即:x&0=00&1=0  1&1=1  即:x&1=xip地址的结构=网络号+主机号若要得到网络号,掩码的结构必然为前面是连续的1,后面是连续的0 通配符0表示严格匹配,1表示无需匹配0和1可以连续也可以不连续通配符与掩码的功能完全不同,没有任何关系例如:如何选择出192.x.1.x这样的

    2022年7月19日
    24
  • 【c#】将DataTable的时间精确到毫秒

    【c#】将DataTable的时间精确到毫秒【c#】将DataTable的时间精确到毫秒

    2022年4月25日
    150

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号