JMeter BeanShell Sampler与JMeter BeanShell断言

前言

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常用的独有方法:

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

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

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 = "123456";

// 调用加密方法
String passwordEncryp = new Md5Encryption().md5Encryption(passwordIni);
vars.put("passwordEncryp",passwordEncryp);

运行结果:
在这里插入图片描述
要注意的是,如果引用的java文件中,又依赖了其他jar包,那么也需要将相应的jar包导入JMeter的安装目录的/lib/etc中并重启JMeter,引用.class和.jar也是同理。

2.2 addClassPath

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

// 添加路径:类所在项目的目录
addClassPath("E:/project/workspace/mutest/src");

// 引入
import mutest.Md5Encryption;

String passwordIni = "123456";
// 调用加密方法
String passwordEncryp = new Md5Encryption().md5Encryption(passwordIni);
vars.put("passwordEncryp",passwordEncryp);

2.3 引用jar包

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

上面步骤完成后,BeanShell中直接import即可使用:
在这里插入图片描述

// import时带上类的包名
import mutest.Md5Encryption;

String passwordIni = "123456";
// 调用加密方法
String passwordEncryp = new Md5Encryption().md5Encryption(passwordIni);
vars.put("passwordEncryp",passwordEncryp);

3 BeanShell断言

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

例如下面的例子,登录失败,但没有添加断言,且接口响应码是200,所以接口被JMeter判定为成功。
在这里插入图片描述
根据接口定义(一般由开发提供的接口文档定义),登录成功后,返回内容的message是“操作成功”,于是我们给接口添加一个【响应断言】
在这里插入图片描述
再次运行,可以看到接口被判定为失败:
在这里插入图片描述
对于这种校验比较简单的接口,【响应断言】能够满足要求,但遇到结果校验很复杂的接口,【响应断言】就无法胜任了,这时候就要用到【BeanShell断言】了。

3.1 校验JSONObject

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

{
	"code" : 0,
	"goodsInfo" : {
		"name" : "computer",
		"price" : 4500,
		"size" : 60
	}
}

1、首先,我们用BeanShell取样器模拟接口返回特定响应结果:
在这里插入图片描述
假如这个数据是根据id=1(id是接口的一个入参)的条件获取的,那么我们就可以编写sql语句去数据库查询数据了。
数据库中的数据:
在这里插入图片描述
2、构造【JDBC Request】去获取数据库数据:
在这里插入图片描述
【BeanShell断言】,JSON处理用得是阿里的fastjson,jar包自行下载后放入JMeter的安装目录的/lib/etc中并重启JMeter
在这里插入图片描述
运行后,查看结果:
在这里插入图片描述
附上【BeanShell断言】代码

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}]}";

上面的数据,对应数据库里status=1的数据:
在这里插入图片描述
针对这种需求,sql语句要修改一下:
在这里插入图片描述
这个接口结果校验的难点在于,接口数据与数据库数据的顺序不对应,且无法事先确定其顺序,如果直接对比两个JSONObject,由于JSONArray中元素顺序不同,会导致对比返回false,尽管两个JSONObject中的数据除了顺序,其他都相同。

要解决顺序问题,我想到的方案是对数据进行进一步加工,将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的相关知识,后续还会更新更多的使用场景。
  • 35
    点赞
  • 157
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
### 回答1: JMeter BeanShell Sampler是一种JMeterSampler,它使用BeanShell脚本语言编写。BeanShell是一种Java编写的脚本语言,它可以在JVM上运行,并且可以访问Java类和接口。使用BeanShell脚本,可以在JMeter中编写自定义的Sampler,以便执行更复杂的测试任务。BeanShell Sampler可以通过JMeter的GUI界面或者JMeter的脚本文件进行配置和使用。 ### 回答2: JMeter是一个性能测试工具,可以模拟大量用户对Web应用程序的访问,测试系统的性能。Beanshell Sampler则是JMeter的一个非常有用的组件,它允许测试人员使用Beanshell脚本来执行复杂的测试逻辑或对测试数据进行处理。 使用Beanshell Sampler的第一步是将它添加到JMeter测试计划中。可以在JMeter工具栏中选择“Sampler”菜单,然后选择“Beanshell Sampler”。在Beanshell Sampler配置页面中,可以编写自定义的Beanshell脚本。脚本可以使用类Java语法编写,可以使用Beanshell语法,也可以使用Java类库。 一个简单的Beanshell脚本可以像这样: ``` log.info("Hello, World!"); ``` 这个简单的脚本会将“Hello, World!”信息写入到JMeter的日志文件中。 Beanshell脚本既可以在每个请求之前执行,也可以在每个请求之后执行。默认情况下,Beanshell Sampler在每个请求之前执行。如果需要在每个请求之后执行,则需要将“Run thread group as a loop”选项设置为False,并将“Action to be taken after a sampler error”选项设置为“Continue”. 在脚本中,可以使用很多JMeter提供的对象和方法,如vars, log, prev,ctx等。vars对象允许在脚本中使用JMeter变量,log对象可以记录日志信息。 ``` log.info("Response code: " + prev.getResponseCode()); log.info("Response message: " + prev.getResponseMessage()); ``` prev对象包含了上一个请求的响应数据,ctx对象包含了JMeter上下文信息。 除了使用JMeter内置对象和方法,Beanshell还可以使用Java类库的方法,只需要将需要使用的Java类库的jar文件放置在JMeter的lib目录下即可。 总之,Beanshell Sampler是JMeter非常有用的组件,通过编写Beanshell脚本,可以实现很多复杂的测试逻辑和数据处理操作,从而更好地完成性能测试任务。 ### 回答3: JMeter Beanshell Sampler是JMeter的一个组件,可以用于执行任意Java代码。使用JMeter Beanshell Sampler可以方便地对JMeter进行扩展和自定义。在JMeter中,可以通过以下步骤使用Beanshell Sampler: 1. 新建一个测试计划,然后选择添加“Thread Group”组件。 2. 在“Thread Group”组件中添加“Beanshell Sampler”组件。 3. 在“Beanshell Sampler”组件中编写Java代码。 4. 运行测试计划,查看测试结果。 在编写Beanshell脚本时,可以使用JMeter提供的Beanshell变量和方法,也可以使用自定义变量和方法。此外,还可以在Beanshell脚本中调用外部的Java类和方法。需要注意的是,Beanshell脚本的性能要比纯Java代码稍差,因为Beanshell是解释执行的。 在使用Beanshell Sampler时,可以实现一些常见的场景,例如: 1. 计算某些数据后再发送HTTP请求。 2. 根据条件控制请求的发送次数。 3. 根据特定的逻辑生成请求数据。 4. 在JMeter中实现自定义函数等功能。 需要注意的是,在使用Beanshell Sampler时需要谨慎操作,避免因为编写不当而导致性能问题或安全问题。因此,编写Beanshell脚本时需要遵循Java的最佳实践,尽量减少不必要的计算和I/O操作,同时进行充分的测试和验证。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

云深i不知处

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

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

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

打赏作者

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

抵扣说明:

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

余额充值