跨线程组数据传递
在JMeter测试中,线程组间相互独立,普通本地变量无法跨域引用。跨线程组数据传递的核心是借助全局作用域容器存储数据,再通过读取逻辑复用。
一、核心实现方法
以下方法均不依赖数据库,按易用性和场景适配度排序,代码块均为Typora原生格式,可直接复制使用。
(一)方法一:全局属性(props)+ 函数(动态单条数据首选)
借助JMeter内置props全局属性对象(全进程可见),搭配函数生成动态数据,通过BeanShell脚本读写,适配token、会话ID等单条动态数据传递。
- 核心原理
props是java.util.Properties类型全局对象,键值对存储,支持所有线程组读写。结合__UUID、__time等函数可快速生成动态数据,无需外部组件。
- 实战示例
场景:线程组A获取接口token并生成唯一标识,传递给线程组B作为请求参数。
线程组A(数据写入端)
- 添加HTTP请求:调用登录接口,通过JSON提取器提取
token,存入本地变量local_token。 - 添加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(数据读取端)
- 添加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);
-
HTTP请求参数化:直接引用
${req_token}、${req_uniqueId}即可复用数据。 -
核心函数说明
__UUID():生成36位唯一标识,适用于订单号、请求ID。__time(format,):自定义格式生成时间戳,如yyyyMMddHHmmss。__log(message,):打印日志到控制台,用于调试数据传递结果。__V(variableName,):引用嵌套/动态变量名,解决复杂变量引用问题。
(二)方法二:全局用户定义变量 + 函数(静态/半动态数据)
将用户定义变量设为全局作用域,搭配__P、__setProperty函数读写,适配接口域名、固定密钥等静态/半动态数据传递,可视化强、易维护。
-
配置步骤
-
添加全局用户定义变量:右键【测试计划】→ 配置元件 → 用户定义的变量(挂载在测试计划层级,确保全局生效)。
-
数据写入(线程组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"));
- 数据读取(线程组B):
- 直接引用全局变量:
${global_domain}、${global_randomNum}。 __P函数读取:${__P(global_randomNum,0)}(第二个参数为默认值,避免读取失败)。
(三)方法三:文件读写 + 函数(批量数据传递)
通过BeanShell脚本读写本地TXT/CSV文件,搭配__CSVRead、__StringFromFile函数,适配多组账号、商品ID等批量数据传递,无数据库依赖。
- 实战示例(CSV文件)
场景:线程组A生成10条测试数据写入CSV,线程组B读取数据批量调用接口。
- 线程组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();
- 线程组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}批量调用接口。
- 核心函数说明
__RandomString(length,chars,):生成指定长度、字符集的随机字符串,适用于密码。__CSVRead(filePath,columnIndex):直接读取CSV指定列数据,无需额外组件。__StringFromFile(filePath,encoding,):读取TXT文件内容,适配纯文本批量数据。
(四)方法四:Test Action + 全局变量(控制执行顺序+传递)
结合Test Action组件控制线程组执行顺序,搭配全局属性传递数据,适配需严格按顺序执行的场景(如初始化后再执行业务),函数优化数据处理。
-
配置步骤
-
测试计划设置:勾选“独立运行每个线程组”,确保线程组A执行完再执行线程组B。
-
线程组A(数据写入):
- 添加HTTP请求:调用初始化接口,提取
init_param变量。 - 添加BeanShell后置处理程序,脚本如下:
String initParam = vars.get("init_param");
// __trim函数去除空格,清洗数据
String cleanParam = "${__trim(${initParam},)}";
props.put("cross_initParam", cleanParam);
- 线程组B(数据读取+执行控制):
- 添加Test Action组件:设置暂停时间(可选),确保数据写入完成。
- 添加BeanShell前置处理程序,脚本如下:
String initParam = props.get("cross_initParam");
// __eval函数解析变量,适配特殊字符
String finalParam = "${__eval(${initParam},)}";
vars.put("req_initParam", finalParam);
- HTTP请求:引用
${req_initParam}执行业务接口。
二、核心函数汇总表
| 函数名称 | 语法格式 | 核心作用 | 适配场景 |
|---|---|---|---|
| __UUID | $ | 生成36位唯一标识符 | 订单号、请求ID、唯一账号 |
| __time | $ | 自定义格式生成时间戳 | 时间参数、唯一标识后缀 |
| __Random | $ | 生成指定范围随机整数 | 动态ID、随机数量参数 |
| __RandomString | $ | 生成自定义随机字符串 | 密码、验证码、随机备注 |
| __setProperty | $ | 设置全局属性 | 快速写入全局数据,无需脚本 |
| __P | $ | 读取全局属性,支持默认值 | 与__setProperty配对使用 |
| __V | $ | 引用嵌套/动态变量 | 复杂变量名场景 |
| __trim | $ | 去除变量前后空格 | 数据清洗,避免接口报错 |
三、避坑要点
- 执行顺序控制:依赖顺序传递时,务必勾选“独立运行每个线程组”,按“写入在前、读取在后”排列线程组,可搭配Test Action等待功能。
- 变量作用域区分:
vars仅作用于当前线程组,props全局生效,避免混用;函数调用需确保变量已生成。 - 高并发安全:多线程同时写入全局属性/文件时,需加同步锁(
synchronized (this) {}),文件用绝对路径,避免同时读写。 - 函数使用规范:参数完整(如
__Random需填最小/最大值),特殊字符转义(文件路径用//),避免脚本中嵌套过多函数。 - 临时数据清理:文件传递场景下,测试后通过BeanShell脚本删除临时文件,避免残留数据影响下次测试。
- 数据类型一致:确保读写数据类型一致,可通过
__eval、__trim函数适配,避免接口请求报错。
四、典型场景串
场景:多线程组完成“注册-登录-下单”全流程,跨线程传递账号、token,函数实现动态数据生成。
- 线程组A(用户注册):
__UUID()生成唯一手机号(截取前11位):${__UUID()}。__RandomString(6,1234567890)生成验证码,调用注册接口。- BeanShell脚本写入全局属性:
props.put("cross_phone", phone)、props.put("cross_pwd", pwd)。
- 线程组B(用户登录):
__P函数读取账号密码:${__P(cross_phone,)}、${__P(cross_pwd,)},调用登录接口。- 提取token,
__setProperty写入全局:${__setProperty(cross_token,${token},)}。
- 线程组C(下单接口):
__P函数读取token:${__P(cross_token,)},放入请求头。__Random(1,100)生成商品ID,__time(yyyyMMddHHmmss)生成订单时间,调用下单接口。

浙公网安备 33010602011771号