JMeter案例分享:获取响应中的json键值动态生成json文件并上传

目录

前言

业务需求

分析接口执行逻辑

思路:4个步骤

1、通过JSON Extractor,逐个地提取所需要的字段

2、通过BeanShell后置处理器拼接json格式字符串

3、将拼接好的Json格式的企业信息数据写入文件

4、上传生成的文件


前言

一般来说,在JMeter脚本中,我们需要上传文件时,都会在本地保存一批固定的数据文件,反复执行上传;

需要传递Json格式的参数时,会通过Sampler的Body Data上传,在其中可以方便地进行关联及参数化。

然而,在前文content-type中的multipart/form-data中的案例,却是以上二者的综合体:其中的第三部分要上传的文件不是一个静态json文本,而是需要在运行时根据前面请求返回的响应,动态生成所需要的json文件,然后再在请求中,将该json文件上传。其内容大致如下(json键值稍做删减)

------WebKitFormBoundary4FB5e6lCzhHGTwV3
Content-Disposition: form-data; name="model"; filename="blob"
Content-Type: application/json

{
	"authorizedPersonRequestParam": {
		
	},
	"enterpriseRequestParam": {
		"id": "12345"
	}
}

业务需求

这里先详述一下业务需求,以便大家更清晰地理解脚本需求。

系统用户为企业管理员,需要变更企业信息。登录后选择企业信息,点击“编辑”,会读取当前系统保存的企业数据,用户进行修改后提交。

分析接口执行逻辑

使用Fiddler抓包,分析接口入参及响应数据后,梳理出接口的执行逻辑如下:

1、发送企业数据查询请求,查询企业的当前信息。

2、系统以json格式返回企业数据信息,包含企业名称、统一社会信用代码、联系人、注册地等几十条文字信息,以及该企业曾上传的营业执照、法人身份证等图片信息。

3、用户修改后,发送提交请求,更新企业信息。此时,因需要将步骤2中提及的各文字信息以及附件图片信息一并提交,所以使用的是上一篇文章中所提到的multipart/form-data格式提交数据。

前文说过,整个表单分为4个部分,其中第(1)、(2)、(4)部分都是固定值,分别使用对应的形式传递参数或者上传文件即可。但是第(3)部分,也就是页面表单中的所有企业信息的文本数据,将打包成一个json格式的文件上传,其中包含企业名称,统一社会信用代码等唯一值,系统进行严格校验,必须使用真实有效的数据。所以,不可以使用固定的文件,需要在执行过程中动态地获取到职业信息,实时生成所需文件后上传。幸运的是,这部分数据,都可以从步骤2中获取。

思路:4个步骤

所以我的思路如下:

1、从上面第二步的查询请求的响应中,获取到第三步目标json文件中所需要的所有数据

2、拼接成目标Json格式的文本

3、将拼接好的Json格式的企业信息数据写成一个文件

4、按照上篇中介绍的方法,上传Json文件

下面逐步解决。

1、通过JSON Extractor,逐个地提取所需要的字段

其实有几十个字段,一个屏幕显示不下。这里只贴一部分示意一下。

    

2、通过BeanShell后置处理器拼接json格式字符串

当然,也可以使用前置处理器,放到提交接口中。按照提交接口所需要的Json格式拼接json字符串,使用上一步提取出来的数据,一个一个的拼接其中的变量。

我贴一部分代码在这里,大家理解如何拼接即可(因为你需要拼接的json串肯定跟我的结构不一样,贴全部代码毫无意义):

StringBuilder ori_data = new StringBuilder("{\"authorizedPersonRequestParam\":{},\"enterpriseRequestParam\":{\"id\":\"");
ori_data.append("${id}");
ori_data.append("\"}}");

以上代码,就拼接出了下面的json串

{"authorizedPersonRequestParam":{},"enterpriseRequestParam":{"id":"12345"}}

格式化一下,长这样(其中的变量${id}在运行过程中会被替换成实际值):

{
	"authorizedPersonRequestParam": {
		
	},
	"enterpriseRequestParam": {
		"id": "12345"
	}
}

3、将拼接好的Json格式的企业信息数据写入文件

import org.apache.jmeter.services.FileServer;
import java.io.*;
import java.nio.charset.Charset;

//获取Jmeter项目路径,以便将文件写到本项目根目录下
String jmxFileDir = FileServer.getFileServer().getBaseDir();
File file = new File(jmxFileDir,"blob"); //bolb是要保存的文件名称,自定义

//将前面拼接出来的字符串写到文件中
FileWriter fstream = new FileWriter(file,Charset.forName("UTF-8"),false);   //以覆盖方式写文件,确保每一次迭代都把上一次的数据清空
BufferedWriter out = new BufferedWriter(fstream);	
out.write(ori_data.toString());

//关闭文件流
jmxFileDir = null;
ori_data = null;
file = null;
out.close();
fstream.close();

4、上传生成的文件

至此,结合本篇开头提及的那篇文章,脚本编制完成,单线程调试成功。然而,2个线程并发的时候,发现又出错了。。。

原来,步骤3写文件时,使用了固定的文件名blob。导致并发时各线程都在写这一个文件,而上传时,不同用户也在上传这一个文件,里面的企业信息肯定错误。

于是,重新修正:

1)步骤3写文件时,文件名加${__threadNum},以区分不同线程的文件,修改后如下:

import org.apache.jmeter.services.FileServer;
import java.io.*;
import java.nio.charset.Charset;

//获取Jmeter项目路径,以便将文件写到本项目根目录下
String jmxFileDir = FileServer.getFileServer().getBaseDir();

//因为并发时每个线程创建一个文件,数量太多,把它们放在一个文件夹下。在根目录下创建一个目录
File blobDir = new File(jmxFileDir,"blob");
if(!blobDir.exists()){
     blobDir.mkdirs();
}

File file = new File(jmxFileDir,"blob${__threadNum}"); //bolb后面添加线程号

//将前面拼接出来的字符串写到文件中
FileWriter fstream = new FileWriter(file,Charset.forName("UTF-8"),false);   //以覆盖方式写文件,确保每一次迭代都把上一次的数据清空
BufferedWriter out = new BufferedWriter(fstream);	
out.write(ori_data.toString());

//关闭文件流
jmxFileDir = null;
ori_data = null;
file = null;
out.close();
fstream.close();

2)上传时,文件名也添加${__threadNum},以区分不同线程的文件

这也就是上一篇截图中,文件名为什么有个${__threadNum},以及我说“在文件名的命名上,可能需要考虑业务并发逻辑”的原因了。

关于更多使用${__threadNum}的小技巧,可参考我的另一篇博文:

JMeter案例分享:使用内置函数threadNum实现线程间数据分离和确保生成不重复的随机数-CSDN博客

后记

后续执行过程中发现,脚本在我这里运行良好,在项目组同事那里写文件错误。检查发现,我用的JDK版本是2.0,他的是1.8,在java8中,FileWriter不支持指定编码格式。以下写文件代码适用于JDK1.8 

import org.apache.jmeter.services.FileServer;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;


String jmxFileDir = FileServer.getFileServer().getBaseDir();

File blobDir = new File(jmxFileDir,"blob");
if(!blobDir.exists()){
     blobDir.mkdirs();
}
File file = new File(blobDir,"blob${__threadNum}");
FileOutputStream fos = new FileOutputStream(file);  
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
osw.write(ori_data.toString());
osw.flush();

jmxFileDir = null;
ori_data = null;
file = null;
fos.close();
osw.close();
  • 17
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值