51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 4215|回复: 0
打印 上一主题 下一主题

[原创] JMeter之使用Bean Shell对请求报文进行参数化

[复制链接]
  • TA的每日心情
    擦汗
    昨天 09:02
  • 签到天数: 1046 天

    连续签到: 4 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2021-4-27 12:12:55 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    在性能测试过程中,为了能够真实模拟用户请求,往往要将请求的报文进行参数化处理。JMeter配置元件与前置处理器都可以进行参数化,但都存在局限性。为了帮助用户更好地进行参数化,JMeter提供了BeanShell取样器。
    BeanShell取样器支持BeanShell脚本语言,这是一种完全符合Java语法规范的脚本语言。下面就以一个样例来说明,如何使用BeanShell来进行参数化。
    1. 需求场景
    有一个TCP服务,接收并处理地理位置上报的报文。报文由以下几部分组成:消息头+消息体+校验码。其中,除了消息体之外,其他部分可以是固定的,消息体包含地理位置的经纬度、速度消息和发送时间。经纬度是长度为8位的16进制字符串,速度消息也是8位的16进制字符串,可以固化,发送时间是时间戳。要求上报的位置消息频率是1秒,每次上报的位置需不同。
    TCP服务接收到位置报文之后,进行业务处理,然后返回一个16进制的报文。
    2. 测试脚本准备
    对于此测试需求,光使用JMeter内置的函数,是不能满足的,因此考虑使用Bean Shell处理,通过编写Java方法的方式进行参数化设置。
    打开JMeter,按以下步骤进行操作。
    (1) 添加线程组
    在测试计划上右键点击->添加->线程(用户)->线程组:
    线程组设置如下:

    线程组名称设为:BeanShell示例。这里设置100个线程,循环1次,也就是要发送100个请求,每个请求的报文都不相同。
    (1) 添加Bean Shell取样器
    在线程组上右键->添加->取样器->Bean Shell取样器:
    添加成功后如下图所示:
    (3) 编写Bean Shell脚本
    Java编写以下脚本
    1. <font size="3">package com.example.rabbitmq.rabbitmq;

    2. import java.text.ParseException;
    3. import java.text.SimpleDateFormat;

    4. public class Baowen {

    5.     // 出发城市的经度
    6.     private static double Depart_City_longitude = 110.330685;
    7.     // 出发城市的纬度
    8.     private static double Depart_City_latitude = 20.035221;

    9.     // 抵达城市的经度
    10.     private static double Arrival_City_longitude = 110.720005;
    11.     // 抵达城市的纬度
    12.     private static double Arrival_City_latitude = 19.610221;
    13.     // 两个城市之间单程的位置点数
    14.     private static int count = 5000;

    15.     private static String dateFormate = "yyMMddHHmmss";
    16.     private static String baseTime = "200609153143";
    17.     // 报文前缀
    18.     private static String beginMsg = "02004022018986042206198026706200010000000000000003";
    19.     private static String midMsg = "000B00020111";
    20.     // 报文后缀
    21.     private static String endMsg = "010400000000EE7E";

    22.     // 经度上移动的步频
    23.     private static Double getLongitudeStep() {
    24.         return (Depart_City_longitude - Arrival_City_longitude) /count;
    25.     }

    26.     // 纬度上移动的步频
    27.     private static Double getlatitudeStep() {
    28.         return (Depart_City_latitude - Arrival_City_latitude) /count;
    29.     }</font>
    复制代码
    1. <font size="3"> // 把整数转换为长度8位的16进制字符串
    2.     private static String integer2HexString(Integer number) {
    3.         String result = Integer.toHexString(number);
    4.         int len = result.length();
    5.         StringBuilder sb = new StringBuilder("");
    6.         // 不足8位时,前面补0
    7.         if(len < 8) {
    8.             for(int i = len; i < 8; i++) {
    9.                 sb = sb.append("0");
    10.             }
    11.             sb.append(result);
    12.         }
    13.         return sb.toString();
    14.     }

    15.     public static String getLocationMsg(int num) {
    16.         double longitude, latitude;
    17.         // 如果位置点已经到达目的城市,则需返回,上传返程的位置点
    18.         if((Depart_City_longitude - num*getLongitudeStep()) >= Arrival_City_longitude) {
    19.             longitude = Arrival_City_longitude + (num%count) * getLongitudeStep();
    20.             latitude = Arrival_City_latitude + (num%count) * getlatitudeStep();
    21.         } else {
    22.             longitude = Depart_City_longitude - (num%count) * getLongitudeStep();
    23.             latitude = Depart_City_latitude - (num%count) * getlatitudeStep();
    24.         }</font>
    复制代码
    1. <font size="3">// 通过上下文获取JMeter正在运行的线程号
    2. int threadNum = ctx.getThreadNum();
    3. log.info("当前线程号: " + threadNum);
    4. String msg = Baowen.getLocationMsg(threadNum);
    5. log.info("报文: " + msg);
    6. // 通过内置的vars对象,把报文放入变量容器中,变量的key为msg,供后面的脚本使用
    7. vars.put("msg", msg);</font>
    复制代码

    说明:
    1、对时间的处理:以基准时间为参考,每次上报报文的时间,随着线程号的递增,每次递减1000毫秒,这样可以保证间隔1秒的频率发送报文。
    2、对位置的处理:设置往返的两个地点,每次上报一个位置,经纬度都按照指定的步频变化,当经纬度等于或超过终点时,又从终点开始返回,这样来模拟两个城市之间往返。
    3、最后把结果放入JMeter的变量容器中,后续脚本只需以“${msg}”就可以引用该变量(如果获取不到,需要vars.get("msg "))。
    (1) 添加TCP取样器
    在线程组上右键->添加->取样器->TCP取样器
    由于本次测试的是TCP服务,所以这样需要添加一个TCP取样器。TCP取样器配置如下:
    TCPClient classname:  填写TCP报文格式,有三类,这里选择org.apache.jmeter.protocol.tcp.sampler.BinaryTCPClientImpl,是常用的十六进制报文格式。
    Re-use connection:复用TCP长连接请求。TCP长连接的时候需要勾选它。
    EOL(行尾字节值):报文结束标志。因为报文是16进制的,报文的结束标志字符是7e 7e的二进制是 0111 1110,对应的十进制是126。
    (5) 添加察看结果树
        在线程组上右键->添加->监听器->察看结果树:
    (6) 添加聚合报告
    在线程组上右键->添加->监听器->聚合报告
    3. 测试结果
    点击执行按钮,结果如下:


    通过聚合报告可知,请求全部发送成功,并且得到正确的响应。通过察看结果树,对比不同的TCP取样器,可以看到请求报文的差异:
    报文003:020040220189860422061980267062000100000000000000030131b5eb069383d8000B00020111200609153141010400000000EE7E
    报文004:020040220189860422061980267062000100000000000000030131b59606938426000B00020111200609153140010400000000EE7E
    1. BeanShell的应用场景
    以上介绍的是自定义函数场景下对Bean Shell的使用。事实上,Bean Shell还有其他的应用场景。
    (1) 引用外部Java文件
    如果已经有了现成的Java文件,或者需要从外部Java文件中调用方法,那么可以使用Bean Shell引入Java文件,在脚本中使用。
    比如:现有一个Java文件,名为MyJavaFile.java,在Bean Shell脚本中可以这样使用:
    1. <font size="3">// 引入外部Java文件
    2. source("D:/script/MyJavaFile.java")
    3. // 调用Java文件中的方法
    4. String result = new Baowen().getLocation();
    5. // 保存变量
    6. vars.put("msg", result);</font>
    复制代码

    在bean shel中通过source("代码绝对路径")的方式引入java,然后就和普通的Java类一样调用其方法就行
    (1) 引用外部class文件
    这种方式跟上一种类似,先用addClassPath("class文件所在的目录")引入 class文件,然后再用“import包名.类名“的方式导入类,接下来就可以调用class文件当的类和方法了:
    1. <font size="3">// 引入外部class文件
    2. addClassPath("D:\\")
    3. // 导入包和类
    4. Import MyPackeage.Baowen;
    5. // 调用Java文件中的方法
    6. String result = new Baowen().getLocation();
    7. // 保存变量
    8. vars.put("msg", result);</font>
    复制代码

    (3) 引用外部Jar包
    外部Jar包要先导入测试中,可以通过两种方式进行导入。其一,是在测试计划中,通过浏览按钮,将需要导入的jar包引入:
    另外,也可以把Jar包放在JMeter安装目录|lib\ext下。
    然后在Bean Shell中引用:
    1. <font size="3">// 导入包和类
    2. Import MyPackeage.Baowen;
    3. // 调用Java文件中的方法
    4. String result = new Baowen().getLocation();
    5. // 保存变量
    6. vars.put("msg", result);</font>
    复制代码

    Bean Shell是一个小型的、免费的、嵌入式的Java源代码解释器,具有对象脚本语言特性,能够动态地执行标准JAVA语法。并且,Bean Shell支持“松散的”或者动态地指定类型类型。能够在运行时做类型检查,而不需要先定义变量以及指定特定的变量类型来指向变量。
    因为Bean Shell是用java写的,运行在同一个虚拟机的应用程序,因此可以方便地引用脚本中的对象,能够满足复杂的参数化需求。

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-15 00:40 , Processed in 0.071668 second(s), 24 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表