JMeter+Maven接口自动化测试框架设计通用替换参数的方法

Habiba ·
更新时间:2024-09-20
· 584 次阅读

在我们原来的接口自动化测试框架设计之初,已经规划好(约定)如下内容:

1、本框架设计实现接口测试用例数据与脚本分离,

2、jmeter脚本通过csv数据元件获取测试用例数据,

3、excel用例设计列:url、method、params、preResult、sql等预留列,

4、接口请求参数实现参数化,在params设计中使用参数${params}代替,

5、脚本设计在sampler添加beanshell前置处理器,编写替换参数脚本片段,

6、如有多个关联动态数据,可能需要通过sql查询数据并构造正确的请求参数,可能需要后置处理器给下一个接口传参

7、断言脚本设计:a>简单的接口采用响应断言即可,b>有数据变动的接口采用beanshell脚本断言,更准确。

8、至此接口自动化测试框架设计完成,从框架选型、脚本开发到框架搭建,这其中曲折不足为外人道哉!

第五点解析:在每一个Sampler事务请求下添加一个beanshell前置处理器,优先处理需要替换的参数,然后再传变量给sampler,具体实现:通过csv参数元件读取excel用例中的接口请求参数params,在此参数已经设计出哪些需要是被替换的参数进行标识如${mobile}、${practiesId}等等,那么需要替换的参数从何而来呢?根据接口测试用例的步骤可以构造所请求的参数:

1、来源于数据库,在excel用例设计了一列sql,用来执行获取接口请求的某一个数据;

2、来源于上一个接口,即动态参数关联,如token、某个数据Id此类参数;

3、函数自定义,如random随机数、randomString随机字符串、time时间戳等参数;

先来看一版代码:

String data=vars.get("params"); if(data.contains("$1")){ String pre_sql=vars.get("pre_sql"); if( !pre_sql.contains("SELECT") ){ String data=data.replace("$1",vars.get("questionId")); vars.put("new_params",data); }else{ Object questionID=vars.getObject("questionid_set").get(${__Random(0,9,num)}).get("id"); String data=data.replace("$1",questionID.toString()); vars.put("new_params",data); } }else if (data.contains("$userId") && data.contains("$token") ){ String userid = vars.get("userId"); vars.put("new_params",data.replace("$userId",userid)); String data = vars.get("new_params"); vars.put("new_params",data.replace("$token",vars.get("token"))); }else if (data.contains("$token") ){ vars.put("new_params",data.replace("$token",vars.get("token"))); }else if (data.contains("$userId") ){ vars.put("new_params",data.replace("$token",vars.get("userId"))); }else{ vars.put("new_params",data); }

发现上面代码的问题了吗?

1、第一写代码连注释都没有,那么维护难度无疑是难以想象的;

2、java是面向对象的编程语言,代码中多个if...else if...嵌套已是编码大忌;

3、代码存在不规范,可读性差,容易出错,只有自己勉强看得懂;

那么针对上面情况如何解决,一般优化方案如下:

1) 可以进行嵌套,或者多重嵌套,但为保证代码逻辑清晰,提高可读性,尽量不要嵌套(不要在if条件中if条件判断)。

2) 按先后顺序依次判断是否成立,当一个if 语句检测为真,后面的else if 及 else语句都将被跳过,这是共识。

3) 在多分支条件下,若最多只有一个分支条件成立,使用If() {}  else if() {} else if() {} else {} ,且按分支出现概率从大到小排放条件表达式,即概率上出现最多的放在最前面,减少程序判断次数,提高效率。

4) 多分支同时成立且都需要处理的情况下,需使用If() {}  if() {} if() {},怎么看也不见得那么美观。

so,对于优化的点,上面的代码都命中,所以如何才能使之更优化?

记得前面学习python做接口自动化时,有教授一招通过settatr设置内属性和getattr获取内属性值替换参数的方法,其中还是用了正则表达式,那么思路来了?问题也随之而来!

1、java中关于变量(内属性),是需要实现get、set方法的,什么意思?需要提前申明类属性?那我怎么知道每次设计测试用例的请求参数哪些需要进行替换?无疑这个工作量也是耗时的;

2、除非很明确知道哪些参数需要替换、同样的问题存在,要是N个参数呢?它不像python可以通过字符串格式化输出那样调用方法。

问题找到了,那么如何解决问题?

1、不使用复杂的想法设计,可以通过正则匹配需要替换的参数,即约定规范替换参数的格式,如:#mobile#,正则表达式即简单:#(.+?)#

2、使用map、list集合框架,将变量按设计替换的参数顺序存储起来,然后再根据关键key进行设值;

a>第一步将准备的参数进行list储存,设计是有序的,根据params参数变量的数据储存

b>避免出现""空字符串,并且需要先处理掉

c>然后再根据params需要参数化的值当key,list的元素做value,使用map.put(key,value)进行设置;

d>然后再是替换参数,params中需要替换的参数作为key,替换成map对象的value

3、至此接口的请求参数构造完成,需要注意的一点就是list必须按params中#mobile#需要替换的参数有序存储

代码如下(避免出错,一些泛型使用,都采用了Object对象,可以接收更多类型):

public class TestDict { private static String pattern = "#(.+?)#"; final static Logger Log = Logger.getLogger(TestDict.class); /** * 泛型的使用,指代某一种类型,以及不定长传参 当然没必要这么麻烦,可以使用List 就可以了 * * @param args * @return */ // 在声明具有模糊类型(比如:泛型)的可变参数的构造函数或方法时,Java编译器会报unchecked警告。鉴于这些情况,如果程序员断定声明的构造函数和方法的主体不会对其varargs参数执行潜在的不安全的操作,可使用@SafeVarargs进行标记,这样的话,Java编译器就不会报unchecked警告。 // 使用的时候要注意:@SafeVarargs注解,对于非static或非final声明的方法,不适用,会编译不通过。 // 非static申明的方法,可能需要在不定长参数类型前加上:@SuppressWarnings("unchecked") @SafeVarargs public static List getList(T... args) { List list = new ArrayList(); for (int i = 0; i < args.length; i++) { list.add(args[i]); } return list; } /** * list集合去除空字符串元素 * * @param lp * @return */ public static List removeAll(List lp) { List newList = new ArrayList(); for (int i = 0; i < lp.size(); i++) { if (lp.get(i) != "") { newList.add((Object) lp.get(i)); } } return newList; } /** * 将list数组,根据需要替换参数的字符串生成map对象 * * @param content * @param lp * @return */ public static Map TextToDict(String content, List lp) { Map dict = new HashMap(); Pattern pt = Pattern.compile(pattern); Matcher m = pt.matcher(content); int i = 0; while (m.find()) { if (lp.get(i) != "") { dict.put(m.group(1), (Object) lp.get(i)); i++; } i++; } //Log.info("list数组转成map对象"); return dict; } /** * 使用替换的map参数替换json字符串中的参数变量 * * @param content * @param dict * @return */ public static String replaceParam(String content, Map dict) { // boolean isMatch = Pattern.matches(pattern, content); Pattern pt = Pattern.compile(pattern); Matcher m = pt.matcher(content); StringBuffer newContent = new StringBuffer(); while (m.find()) { if (m.group().contains(m.group(1))) { m.appendReplacement(newContent, (String) dict.get(m.group(1))); } } m.appendTail(newContent); return newContent.toString(); } }

测试main函数代码:

public static void main(String[] args) { // json字符串 String content = "{\"mobile\":\"#mobile#\",\"passwd\":\"#passwd#\"}"; // List list = new ArrayList(); // list.add("13800138000"); // list.add(""); // list.add("110022"); // 内置api一行代码去除"" // list.removeAll(Collections.singleton("")); // 在java中支持的泛型 List lp = TestDict.getList("13800138000", "", "110022", 123); System.out.println("初始储存的元素列表:" + lp); List newList = TestDict.removeAll(lp); System.out.println("去除空字符串后的list数组:" + newList); // 测试map对象 // Map map = new HashMap(); // map.put("mobile", "13800138000"); // map.put("age", ""); // map.put("passwd", "110022"); Map dict = TestDict.TextToDict(content, lp); System.out.println("根据json字符串需要替换的参数,和list元素组成key:value键值对:" + dict); String newContent = TestDict.replaceParam(content, dict); System.out.println("再根据新的map对象,json字符串的替换参数与map的key的value进行替换:" + newContent); // ArrayList泛型,定义List能接收所有对象,而不是指定的String或者Integer某一类型的List // List lo = new ArrayList(); // lo.add(100); // lo.add("我来了"); // System.out.println(lo); }

控制台结果输出如下:

初始储存的元素列表:[13800138000, , 110022, 123]

去除空字符串后的list数组:[13800138000, 110022, 123]

2020-04-20 12:00:13 466 [INFO ] TestDict:117 - list数组转成map对象

根据json字符串需要替换的参数,和list元素组成key:value键值对:{passwd=110022, mobile=13800138000}

再根据新的map对象,json字符串的替换参数与map的key的value进行替换:{"mobile":"13800138000","passwd":"110022"}

上面的方法已经完成封装,可以直接投入jmeter脚本开发使用,可能需要调整的再回来修改代码即可。

虽然封装一堆方法,那么在jmeter中如何使用呢?

1、一般导出jar文件放在lib/ext目录下并重启jmeter,bsh脚本开发,import这个jar的包路径,然后初始化类调用方法即可,

2、使用maven构建中编译组件,将我们开发的包自动构建到指定目录lib/ext,

3、重点来了,上面那么多方法,你是不是有点懵,那么我们如何简化上面的代码(不使用封装的方法):

import java.util.regex.Matcher; import java.util.regex.Pattern; //获取变量的值,使用一个变量接收,但此时变量接收后并不能直接作为参数 String mobile=vars.get("phone"); String empty=vars.get("empty"); String passwd=vars.get("number"); //json字符串,此处是举例,应该是excel用例中的params参数 String content = vars.get("params"); //编码时字符串双引号需要转义,使用变量参数时,则不需要 //String params="{\"appVersion\":\"V10.3\",\"channel\":0,\"deviceName\":\"iOS\",\"deviceType\":\"1\",\"deviceid\":\"123456\",\"loginType\":0,\"mobile\":\"#mobile#\",\"password\":\"#passwd#\",\"pushToken\":\"string\",\"systemVersion\":\"1\",\"verifyCode\":\"1\",\"zone\":\"86\"}"; //将接收变量的值赋给一个变量,此时的变量是可以被引用的${param} vars.put("empty",empty); vars.put("passwd",passwd); vars.put("mobile",mobile); //log.info(vars.get("mobile")); //正则表达式 String pattern="#(.+?)#"; //正则表达式匹配编译对象 Pattern pt = Pattern.compile(pattern); Matcher m = pt.matcher(content); //新的字符类型:当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。 StringBuffer newContent = new StringBuffer(); //注意此处就不使用if条件判断, while (m.find()) { // 上面条件满足是否有匹配内容,if判断是否有key if (m.group().contains(m.group(1))) { // 这个追加替换方法,修改了原来的字符串,又不会重新创建字符串 m.appendReplacement(newContent, vars.get(m.group(1))); } } m.appendTail(newContent); //将正则匹配替换换后的参数赋值给新的变量 vars.put("params",newContent.toString());

添加http请求示例,然后将${params}作为新的请求体,请求成功!!!


作者:收集明天的囬忆



自动 自动化 Maven 替换 方法 参数 jmeter 自动化测试 测试 通用 框架

需要 登录 后方可回复, 如果你还没有账号请 注册新账号