Jmeter接口自动化实例(使用Beanshell保存csv文件、csv参数化、setUp线程组)

很久没更新博客了,荒废了很久了,今天更新一下博客,主要记录一下子最近遇到的问题和解决方法👱‍♀️

这篇文章主要记录的是jmeter批量跑接口中遇到的各种疑难,主要涉及到的问题如下

  1. 运行的接口有多个(>=2)其中有一个前提是必须登录才能进行其他接口的操作,但是登录不想每次都运行,该怎么解决呢,请往下看~
  2. 批量执行接口的过程中,需要不同的参数运行接口,使用到了csv参数化
  3. 接口结束之后,需要对响应结果中的一部分信息保留进行数据对比

进入正题,先来解决第一个问题,登录接口只需要运行一次即可,然后其他接口可以使用登录响应结果中的token值进行接口操作。这里jmeter提供了setUp线程可以解决这个问题。

首先在jmeter计划中添加一个线程setUp,把登录接口放置到setUp线程中,然后使用json提取器获取到token信息,并把token信息保存为jmeter变量,如图

登录的响应结果信息如下

{
    "access_token":"dc1dcb1f-725e-4b17-9f47-e1d97477ba11",
    "token_type":"bearer",
    "refresh_token":"307cee12-973e-44af-8022-63949b2c3bd5",
    "expires_in":899,
    "scope":"READ WRITE"
}

这里主要使用json提取器来获取token的变量信息

根据响应结果,使用json提取器,主要用$.access_token提取变量。

然后使用Beanshell后置处理器,将变量保存为jmeter系统变量,代码如下

${__setProperty(all_token,${access_token},false)}

如图

跨线程的其他线程组怎么使用这个变量access_token呢,可以使用http信息头管理器来实现,在http信息头管理器中,添加Authorization来实现,如图

Authorization	Bearer ${__property(all_token,,)}

至此第一个问题已经解决了,那么接下来就是第二个接口的参数化操作了。

这里主要使用了csv进行参数化

csv文本参数信息如图

参数化文本已经准备好了,接下来就是在jmeter中添加csv数据文件设置,如图

说明:

  1. 文件名称:参数化的csv文件

  2. 文件编码:默认可以不填,这里因为有中文,所以使用gb2312,可以根据实际情况设置utf-8或其他

  3. 变量名称:这里设置的变量名称在接口中会使用这个变量信息,所以一定要慎重

如图上所标识,这里的变量名称必须一致。

到这里,已经完成了csv的参数化,那么我们如果获取响应的结果呢?

先来看一下相应的结果的结构是什么吧~

[
    {
        "properties":{
            "cal_name":"云耳",
            "e_type":"food",
            "end_index":12,
            "food_id":8457,
            "kcal_unit_weight":37.95,
            "name":"云耳",
            "start_index":10,
            "time_stamp":1566544101
        },
        "sub_properties":{
            "grams":30,
            "is_default":true,
            "quantifier_id":57,
            "quantity":1,
            "unit":"朵"
        },
        "type":"Entity"
    },
    {
        "properties":{
            "cal_name":"黑木耳",
            "e_type":"food",
            "end_index":9,
            "food_id":8456,
            "kcal_unit_weight":37.95,
            "name":"黑木耳",
            "start_index":6,
            "time_stamp":1566544101
        },
        "sub_properties":{
            "grams":30,
            "is_default":true,
            "quantifier_id":57,
            "quantity":1,
            "unit":"朵"
        },
        "type":"Entity"
    },
    {
        "properties":{
            "cal_name":"木耳",
            "e_type":"food",
            "end_index":2,
            "food_id":5593,
            "kcal_unit_weight":37.95,
            "name":"木耳",
            "start_index":0,
            "time_stamp":1566544101
        },
        "sub_properties":{
            "grams":30,
            "is_default":true,
            "quantifier_id":57,
            "quantity":1,
            "unit":"朵"
        },
        "type":"Entity"
    }
]

当然还可以使用json提取器,来获取变量信息的,使用$[*].properties.cal_name;$[*].properties.name,两个变量之前使用;分开,如图所示

根据之前的使用BeanShell断言判断请求返回的Json相应结果(不同json格式整理)了解到可以使用BeanShell来获取响应结果的变量。

好了,那么接下来就是如何保保存响应结果中需要的信息到csv文本中了。

jmeter中beanshell提供了写入csv文件的方法,主要代码如下

//保存的本地文件
FileWriter fo = new FileWriter("E:\\test\\result_jm.csv",true);
BufferedWriter out = new BufferedWriter(fo);
//需要保存的字段信息
out.write(vars.get("cal")+":"+vars.get("name")+",");
//每个信息完成之后需要换行
out.write(System.getProperty("line.separator"));

out.close();
fo.close();

这里想实现的主要是:对于响应结果非空的数据则保存响应结果到文件1,而对于响应结果为空,则只需要保存输入的参数信息到文件2,以便后期对比确认接口数据准确度。

完整代码如下

import org.json.*;
import org.json.JSONObject;
import org.json.JSONAarry;
//设置文件编码
prev.setDataEncoding("UTF-8");
int i=0;
try{
	log.info("响应结果中的cal_name是"+vars.get("cal"));
	//获取json提取器中的变量cal
	//String cal_test = vars.get("cal");
	String response_data = prev.getResponseDataAsString();//获取请求返回值,此处获取到String类型;
	//JSONObject data_obj = new JSONObject(response_data);//将string类型的返回值构建成JSONObject对象

	//JSONArray data_array = response_data.getJSONArray[0];//返回结果是数组
	JSONArray data_array = new JSONArray(response_data);
	
	int len = data_array.length();//获取data数组的长度
	String strlen = Integer.toString(len);
	log.info("响应结果字符串长度是"+strlen);
	
	String str_array = data_array.toString();
	log.info("响应结果字符串是:"+str_array);
	
	
	
	//cal_test  str_array == null ;cal_test.length() !=null;str_array.equals(0)
	if(len==0){
		log.info("*****************食物匹配失败********************");
		//如果响应结果为空,则只保存food名字到本地文件
		FileWriter fos = new FileWriter("E:\\test\\HB\\food_result2_jm.csv",true);
		BufferedWriter outs = new BufferedWriter(fos);
		outs.write(vars.get("food")+",");
		log.info("原来的食谱名称是:"+ vars.get("food"));
		outs.write(System.getProperty("line.separator"));

		outs.close();
		fos.close();
		
	}else{
		
		log.info("*************响应结果中获取食物cal和name****************");
		
		for(i=0;i<len;i++){
			//返回数组
			JSONObject object = data_array.getJSONObject(i);
			//只获取响应结果中第一个properites的信息,需要取得多个properties,则可以使用for循环
			JSONObject temp_properties = (JSONObject)object.getJSONObject("properties");
			//只获取响应结果中properties中的calname值
			cal_name_string = temp_properties.getString("cal_name");
			log.info("Beanshell 后置处理器的提取的响应cal_name响应结果是:"+ cal_name_string);
			//保存响应结果中的cal_name变量
			vars.put("cal_name_string",cal_name_string);
			
			name_string = temp_properties.getString("name");
			log.info("Beanshell 后置处理器的提取的响应name_string响应结果是:"+ name_string);
			//保存响应结果中的cal_name变量
			vars.put("name_string",name_string);

			log.info("*************食物匹配有结果****************");
			//保存的本地文件,保存响应结果非空的数据
			FileWriter fo = new FileWriter("E:\\test\\HB\\food_result1_jm.csv",true);
			BufferedWriter out = new BufferedWriter(fo);
			out.write(vars.get("name_string")+":"+vars.get("cal_name_string")+":"+vars.get("food")+",");

			log.info("响应结果中食物名称是:"+ vars.get("cal_name_string"));
			log.info("响应结果中食物名称是:"+ vars.get("name_string"));
			log.info("原来的食谱名称是:"+ vars.get("food"));
			out.write(System.getProperty("line.separator"));

			out.close();
			fo.close();
		}
		
	}


	//JSONObject object = data_array.getJSONObject(0);
	//log.info(object);
	//String cal_test = object.getString("cal_name");
	
	
}
catch(Throwable ex){
	log.error("Failed in Beanshell",ex);
	throw ex;
//	//如果响应结果为空,则只保存food名字到本地文件
//	FileWriter fos = new FileWriter("E:\\test\\HB\\food_result2_jm.csv",true);
//	BufferedWriter outs = new BufferedWriter(fos);
//	outs.write(vars.get("food")+",");
//	log.info(vars.get("food"));
//	outs.write(System.getProperty("line.separator"));
//
//	outs.close();
//	fos.close();

	
}

这里使用到了json包,提供一个下载地址:https://repo1.maven.org/maven2/org/json/json/20190722/

ok,到这里jmeter一个完整的流程就结束了,这里有部分jmeter的结果信息,如下

2019-08-23 15:08:28,064 INFO o.a.j.t.JMeterThread: Thread is done: chatbot 1-2628
2019-08-23 15:08:28,064 INFO o.a.j.t.JMeterThread: Thread finished: chatbot 1-2628
2019-08-23 15:08:28,065 INFO o.a.j.u.BeanShellTestElement: 响应结果字符串长度是1
2019-08-23 15:08:28,065 INFO o.a.j.u.BeanShellTestElement: 响应结果字符串是:[{"sub_properties":{"unit":"杯","quantifier_id":32,"quantity":1,"grams":250,"is_default":true},"type":"Entity","properties":{"start_index":0,"time_stamp":1566544108,"cal_name":"野菊","name":"野菊","end_index":2,"kcal_unit_weight":46.8,"food_id":5559,"e_type":"food"}}]
2019-08-23 15:08:28,065 INFO o.a.j.u.BeanShellTestElement: *************响应结果中获取食物cal和name****************
2019-08-23 15:08:28,065 INFO o.a.j.u.BeanShellTestElement: Beanshell 后置处理器的提取的响应cal_name响应结果是:野菊
2019-08-23 15:08:28,065 INFO o.a.j.u.BeanShellTestElement: Beanshell 后置处理器的提取的响应name_string响应结果是:野菊
2019-08-23 15:08:28,065 INFO o.a.j.u.BeanShellTestElement: *************食物匹配有结果****************
2019-08-23 15:08:28,067 INFO o.a.j.u.BeanShellTestElement: 响应结果中食物名称是:野菊
2019-08-23 15:08:28,067 INFO o.a.j.u.BeanShellTestElement: 响应结果中食物名称是:野菊
2019-08-23 15:08:28,067 INFO o.a.j.u.BeanShellTestElement: 原来的食谱名称是:野菊(鲜)
...
2019-08-23 15:08:30,407 INFO o.a.j.t.JMeterThread: Thread is done: chatbot 1-2690
2019-08-23 15:08:30,407 INFO o.a.j.t.JMeterThread: Thread finished: chatbot 1-2690
2019-08-23 15:08:30,522 INFO o.a.j.u.BeanShellTestElement: 响应结果中的cal_name是竹参
2019-08-23 15:08:30,524 INFO o.a.j.u.BeanShellTestElement: 响应结果字符串长度是3
2019-08-23 15:08:30,524 INFO o.a.j.u.BeanShellTestElement: 响应结果字符串是:[{"sub_properties":{"unit":"份","quantifier_id":18,"quantity":1,"grams":200,"is_default":true},"type":"Entity","properties":{"start_index":8,"time_stamp":1566544110,"cal_name":"竹参","name":"竹参","end_index":10,"kcal_unit_weight":155,"food_id":12628,"e_type":"food"}},{"sub_properties":{"unit":"份","quantifier_id":18,"quantity":1,"grams":200,"is_default":true},"type":"Entity","properties":{"start_index":5,"time_stamp":1566544110,"cal_name":"干竹笙","name":"竹笙","end_index":7,"kcal_unit_weight":312.116,"food_id":10653,"e_type":"food"}},{"sub_properties":{"unit":"片","quantifier_id":34,"quantity":1,"grams":7,"is_default":true},"type":"Entity","properties":{"start_index":0,"time_stamp":1566544110,"cal_name":"竹荪","name":"竹荪","end_index":2,"kcal_unit_weight":155,"food_id":5607,"e_type":"food"}}]
2019-08-23 15:08:30,524 INFO o.a.j.u.BeanShellTestElement: *************响应结果中获取食物cal和name****************
2019-08-23 15:08:30,524 INFO o.a.j.u.BeanShellTestElement: Beanshell 后置处理器的提取的响应cal_name响应结果是:竹参
2019-08-23 15:08:30,524 INFO o.a.j.u.BeanShellTestElement: Beanshell 后置处理器的提取的响应name_string响应结果是:竹参
2019-08-23 15:08:30,524 INFO o.a.j.u.BeanShellTestElement: *************食物匹配有结果****************
2019-08-23 15:08:30,526 INFO o.a.j.u.BeanShellTestElement: 响应结果中食物名称是:竹参
2019-08-23 15:08:30,526 INFO o.a.j.u.BeanShellTestElement: 响应结果中食物名称是:竹参
2019-08-23 15:08:30,526 INFO o.a.j.u.BeanShellTestElement: 原来的食谱名称是:竹荪(干)竹笙、竹参
2019-08-23 15:08:30,526 INFO o.a.j.u.BeanShellTestElement: Beanshell 后置处理器的提取的响应cal_name响应结果是:干竹笙
2019-08-23 15:08:30,526 INFO o.a.j.u.BeanShellTestElement: Beanshell 后置处理器的提取的响应name_string响应结果是:竹笙
2019-08-23 15:08:30,526 INFO o.a.j.u.BeanShellTestElement: *************食物匹配有结果****************
2019-08-23 15:08:30,527 INFO o.a.j.u.BeanShellTestElement: 响应结果中食物名称是:干竹笙
2019-08-23 15:08:30,527 INFO o.a.j.u.BeanShellTestElement: 响应结果中食物名称是:竹笙
2019-08-23 15:08:30,527 INFO o.a.j.u.BeanShellTestElement: 原来的食谱名称是:竹荪(干)竹笙、竹参
2019-08-23 15:08:30,527 INFO o.a.j.u.BeanShellTestElement: Beanshell 后置处理器的提取的响应cal_name响应结果是:竹荪
2019-08-23 15:08:30,527 INFO o.a.j.u.BeanShellTestElement: Beanshell 后置处理器的提取的响应name_string响应结果是:竹荪
2019-08-23 15:08:30,527 INFO o.a.j.u.BeanShellTestElement: *************食物匹配有结果****************
2019-08-23 15:08:30,528 INFO o.a.j.u.BeanShellTestElement: 响应结果中食物名称是:竹荪
2019-08-23 15:08:30,528 INFO o.a.j.u.BeanShellTestElement: 响应结果中食物名称是:竹荪
2019-08-23 15:08:30,528 INFO o.a.j.u.BeanShellTestElement: 原来的食谱名称是:竹荪(干)竹笙、竹参
2019-08-23 15:08:30,528 INFO o.a.j.t.JMeterThread: Thread is done: chatbot 1-2685
2019-08-23 15:08:30,528 INFO o.a.j.t.JMeterThread: Thread finished: chatbot 1-2685
2019-08-23 15:08:30,529 INFO o.a.j.e.StandardJMeterEngine: Notifying test listeners of end of test
2019-08-23 15:08:30,529 INFO o.a.j.s.FileServer: Close: E:\test\HB\qa.csv
2019-08-23 15:08:30,529 INFO o.a.j.s.FileServer: Close: E:\test\HB\food_jm.csv
2019-08-23 15:08:30,529 INFO o.a.j.g.u.JMeterMenuBar: setRunning(false, *local*)

总结:以上就是jmeter接口测试所遇到的问题以及如何解决的~,这里还有一点小问题,因为参数化的时候,是中文信息,所以开始的时候一直都是乱码,这里说一下有几个办法,给大家一个参考。

第一个方法:修改jmeter.properties文件中的编码格式为utf-8sampleresult.default.encoding=UTF-8

第二个方法,接口信息中设置编码格式为utf-8,如图

第三个方法:在beanshell中设置编码格式为utf-8,//设置文件编码
prev.setDataEncoding("UTF-8");

还有可能不是编码格式的问题,而是字体支持的原因,可以修改jmeter.properties中的jsyntaxtextarea.font.family=宋体可以试试看看能不能解决。

posted @ 2019-08-23 16:48  janehost  阅读(2378)  评论(0编辑  收藏  举报