跨线程组数据传递

在JMeter测试中,线程组间相互独立,普通本地变量无法跨域引用。跨线程组数据传递的核心是借助全局作用域容器存储数据,再通过读取逻辑复用。

一、核心实现方法

以下方法均不依赖数据库,按易用性和场景适配度排序,代码块均为Typora原生格式,可直接复制使用。

(一)方法一:全局属性(props)+ 函数(动态单条数据首选)

借助JMeter内置props全局属性对象(全进程可见),搭配函数生成动态数据,通过BeanShell脚本读写,适配token、会话ID等单条动态数据传递。

  1. 核心原理

propsjava.util.Properties类型全局对象,键值对存储,支持所有线程组读写。结合__UUID__time等函数可快速生成动态数据,无需外部组件。

  1. 实战示例

场景:线程组A获取接口token并生成唯一标识,传递给线程组B作为请求参数。

线程组A(数据写入端)

  1. 添加HTTP请求:调用登录接口,通过JSON提取器提取token,存入本地变量local_token
  2. 添加BeanShell后置处理程序,脚本如下:
// 读取本地变量(接口提取的token)
String token = vars.get("local_token");
// __UUID函数生成唯一标识
String uniqueId = "${__UUID()}"; 
// __time函数生成时间戳(格式:yyyyMMddHHmmss)
String timeStamp = "${__time(yyyyMMddHHmmss,)}";
// 写入全局属性(键名加前缀防冲突)
props.put("cross_token", token);
props.put("cross_uniqueId", uniqueId);
props.put("cross_timeStamp", timeStamp);
// __log函数打印日志调试
${__log("线程组A写入全局数据:token="+token+",唯一标识="+uniqueId,)};

线程组B(数据读取端)

  1. 添加BeanShell前置处理程序,脚本如下:
// 读取全局属性
String globalToken = props.get("cross_token");
String globalUniqueId = props.get("cross_uniqueId");
// 存入本地变量供接口使用
vars.put("req_token", globalToken);
vars.put("req_uniqueId", globalUniqueId);
// __V函数引用嵌套动态变量名
String dynamicKey = "cross_timeStamp";
String globalTimeStamp = props.get("${__V(dynamicKey,)}");
vars.put("req_timeStamp", globalTimeStamp);
  1. HTTP请求参数化:直接引用${req_token}${req_uniqueId}即可复用数据。

  2. 核心函数说明

  • __UUID():生成36位唯一标识,适用于订单号、请求ID。
  • __time(format,):自定义格式生成时间戳,如yyyyMMddHHmmss
  • __log(message,):打印日志到控制台,用于调试数据传递结果。
  • __V(variableName,):引用嵌套/动态变量名,解决复杂变量引用问题。

(二)方法二:全局用户定义变量 + 函数(静态/半动态数据)

将用户定义变量设为全局作用域,搭配__P__setProperty函数读写,适配接口域名、固定密钥等静态/半动态数据传递,可视化强、易维护。

  1. 配置步骤

  2. 添加全局用户定义变量:右键【测试计划】→ 配置元件 → 用户定义的变量(挂载在测试计划层级,确保全局生效)。

  3. 数据写入(线程组A):

- 静态数据:直接添加键值对(如global_domain=www.test.com),所有线程组直接引用。

- 半动态数据:通过函数+脚本联动写入,脚本如下:

// __Random函数生成1000-9999随机数
int randomNum = ${__Random(1000,9999,)};
// __setProperty函数直接写入全局变量
${__setProperty(global_randomNum,${randomNum},)};
// 或通过脚本写入全局属性
vars.put("local_random", String.valueOf(randomNum));
props.put("global_randomNum", vars.get("local_random"));
  1. 数据读取(线程组B):
  • 直接引用全局变量:${global_domain}${global_randomNum}
  • __P函数读取:${__P(global_randomNum,0)}(第二个参数为默认值,避免读取失败)。

(三)方法三:文件读写 + 函数(批量数据传递)

通过BeanShell脚本读写本地TXT/CSV文件,搭配__CSVRead__StringFromFile函数,适配多组账号、商品ID等批量数据传递,无数据库依赖。

  1. 实战示例(CSV文件)

场景:线程组A生成10条测试数据写入CSV,线程组B读取数据批量调用接口。

  1. 线程组A(写入CSV):

添加循环控制器(循环10次),内部添加BeanShell取样器,脚本如下:

// __time函数生成唯一用户名,__RandomString生成随机密码
String username = "user_${__time(yyyyMMddHHmmssSSS,)}";
String password = "${__RandomString(8,abcdefghijklmnopqrstuvwxyz1234567890,)}";
// 写入CSV文件(绝对路径)
String filePath = "D:/jmeter_data/batch_data.csv";
FileWriter fw = new FileWriter(filePath, true); // 追加写入
BufferedWriter bw = new BufferedWriter(fw);
bw.write(username + "," + password); // 逗号分隔列
bw.newLine();
bw.close();
fw.close();
  1. 线程组B(读取CSV):

方式一:通过CSV数据文件设置组件

  • 文件名:D:/jmeter_data/batch_data.csv
  • 变量名:csv_username,csv_password
  • 勾选“遇到文件结束符时循环”,实现批量复用。

方式二:__CSVRead函数直接读取(无需组件)

  • 读取用户名:${__CSVRead(D:/jmeter_data/batch_data.csv,0)}(0为第一列)
  • 读取密码:${__CSVRead(D:/jmeter_data/batch_data.csv,1)}(1为第二列)
  • HTTP请求:引用${csv_username}${csv_password}批量调用接口。
  1. 核心函数说明
  • __RandomString(length,chars,):生成指定长度、字符集的随机字符串,适用于密码。
  • __CSVRead(filePath,columnIndex):直接读取CSV指定列数据,无需额外组件。
  • __StringFromFile(filePath,encoding,):读取TXT文件内容,适配纯文本批量数据。

(四)方法四:Test Action + 全局变量(控制执行顺序+传递)

结合Test Action组件控制线程组执行顺序,搭配全局属性传递数据,适配需严格按顺序执行的场景(如初始化后再执行业务),函数优化数据处理。

  1. 配置步骤

  2. 测试计划设置:勾选“独立运行每个线程组”,确保线程组A执行完再执行线程组B。

  3. 线程组A(数据写入):

  • 添加HTTP请求:调用初始化接口,提取init_param变量。
  • 添加BeanShell后置处理程序,脚本如下:
String initParam = vars.get("init_param");
// __trim函数去除空格,清洗数据
String cleanParam = "${__trim(${initParam},)}";
props.put("cross_initParam", cleanParam);
  1. 线程组B(数据读取+执行控制):
  • 添加Test Action组件:设置暂停时间(可选),确保数据写入完成。
  • 添加BeanShell前置处理程序,脚本如下:
String initParam = props.get("cross_initParam");
// __eval函数解析变量,适配特殊字符
String finalParam = "${__eval(${initParam},)}";
vars.put("req_initParam", finalParam);
  1. HTTP请求:引用${req_initParam}执行业务接口。

二、核心函数汇总表

函数名称 语法格式 核心作用 适配场景
__UUID $ 生成36位唯一标识符 订单号、请求ID、唯一账号
__time $ 自定义格式生成时间戳 时间参数、唯一标识后缀
__Random $ 生成指定范围随机整数 动态ID、随机数量参数
__RandomString $ 生成自定义随机字符串 密码、验证码、随机备注
__setProperty $ 设置全局属性 快速写入全局数据,无需脚本
__P $ 读取全局属性,支持默认值 与__setProperty配对使用
__V $ 引用嵌套/动态变量 复杂变量名场景
__trim $ 去除变量前后空格 数据清洗,避免接口报错

三、避坑要点

  1. 执行顺序控制:依赖顺序传递时,务必勾选“独立运行每个线程组”,按“写入在前、读取在后”排列线程组,可搭配Test Action等待功能。
  2. 变量作用域区分vars仅作用于当前线程组,props全局生效,避免混用;函数调用需确保变量已生成。
  3. 高并发安全:多线程同时写入全局属性/文件时,需加同步锁(synchronized (this) {}),文件用绝对路径,避免同时读写。
  4. 函数使用规范:参数完整(如__Random需填最小/最大值),特殊字符转义(文件路径用//),避免脚本中嵌套过多函数。
  5. 临时数据清理:文件传递场景下,测试后通过BeanShell脚本删除临时文件,避免残留数据影响下次测试。
  6. 数据类型一致:确保读写数据类型一致,可通过__eval__trim函数适配,避免接口请求报错。

四、典型场景串

场景:多线程组完成“注册-登录-下单”全流程,跨线程传递账号、token,函数实现动态数据生成。

  1. 线程组A(用户注册)
    1. __UUID()生成唯一手机号(截取前11位):${__UUID()}
    2. __RandomString(6,1234567890)生成验证码,调用注册接口。
    3. BeanShell脚本写入全局属性:props.put("cross_phone", phone)props.put("cross_pwd", pwd)
  2. 线程组B(用户登录)
    1. __P函数读取账号密码:${__P(cross_phone,)}${__P(cross_pwd,)},调用登录接口。
    2. 提取token,__setProperty写入全局:${__setProperty(cross_token,${token},)}
  3. 线程组C(下单接口)
    1. __P函数读取token:${__P(cross_token,)},放入请求头。
    2. __Random(1,100)生成商品ID,__time(yyyyMMddHHmmss)生成订单时间,调用下单接口。
posted @ 2026-01-23 15:39  向闲而过  阅读(1)  评论(0)    收藏  举报