尧图精选

JMeter参数传递全攻略:提取器、CSV与跨线程组实战

🕒 发布时间:2026/10/1 4:40:26 📁 来源:尧图网络
刚入行做接口测试那会儿我最大的困惑就是登录接口返回了一个token下一个接口的请求头要怎么拿到这个token总不能每跑一轮都手动复制粘贴吧。后来才明白这其实是JMeter参数传递的基本功。所谓参数传递就是把一个请求里的变量值、响应里的返回值、或者外部文件里的数据通过某种方式交给后续请求使用。JMeter里最常用的做法归纳下来就是三种配置元件级别的静态变量、提取器级别的响应关联、数据文件级别的批量参数化。这篇文章就把这三种方法掰开揉碎讲清楚另外附上数据库结果传参和跨线程组传参的进阶玩法照着做就能上手。1. 先理解参数传递JMeter里数据是怎么流动的1.1 三种方法对应三种典型场景我见过很多新手一上来就问怎么传参其实这个问题得先拆成场景来回答。参数传递在你的测试脚本里通常解决三种问题第一全局静态参数。比如测试环境的IP、端口、用户名、密码这些值在整个测试计划里基本不变只是希望在脚本里不写死方便以后切换环境。这种情况下用用户定义的变量最省事。第二接口关联。这是最核心的场景。上一个接口的响应里带着token、订单号、ID这类动态数据下一个接口必须用这个数据才能跑通。比如登录拿token、创建订单拿订单号、查询订单状态。这种场景要用提取器从响应报文里把值抠出来传给后面的请求。第三批量数据参数化。压测或者跑一批不同账号的用例时需要从外部文件读入大量测试数据每次迭代取一行。这种场景用CSV数据文件也就是常说的人话把账号密码存在一个文本文件里让JMeter循环着读。把场景先分清楚方法基本就定了。这篇文章的核心也是对号入座静态参数找方法一接口关联找方法二批量数据找方法三。1.2 ${}变量引用的底层逻辑无论哪种方法最后传递数据的载体都是同一个东西JMeter变量。而使用变量的语法就是${变量名}。这个语法你可能已经在很多教程里见过了但它的作用域规则最容易被忽略。JMeter变量的作用域分三级。最低是取样器作用域比如一个HTTP请求里定义的变量只在这个请求内有效。中间是线程组作用域变量在该线程组内的所有请求里都能用但是这个线程组和另一个线程组之间是隔离的。最高是测试计划作用域几乎整个脚本都能拿到。我打个比方一个线程组相当于一个独立运行的工厂车间车间里产生的中间产品只能在本车间用想给另一个车间用就必须送到仓库这个仓库就是JMeter属性property。后面讲的跨线程组传递本质就是送仓库的操作。理解了这个作用域模型很多传参报错都能自己排查了。比如你用提取器从A线程组提取了token在B线程组里引用发现值是空的原因就是变量作用域隔离两个线程之间根本看不到对方。2. 方法一用户定义变量——全局静态参数的标配2.1 怎么添加和使用这是JMeter里最简单、也最应该优先掌握的参数化方式。添加路径是右键测试计划 → 添加 → 配置元件 → 用户定义的变量。打开面板后你会看到一个表格左边填变量名右边填变量值。举个例子我测试报销系统时会在里面维护这样一组值变量名变量值说明protocolhttp协议host192.168.1.100环境地址port8080服务端口usernametest01测试账号password123456测试密码然后在HTTP请求的服务器名称或IP里填${host}端口填${port}请求体里提交参数用${username}、${password}。脚本写好后想切换预发布环境只需要把host和port改一下所有请求自动跟着变。我做项目交接的时候最怕看到别人脚本里硬编码了一堆IP环境一变就得满世界找哪里写了地址。用用户定义变量做全局配置在团队协作里非常关键。2.2 和函数助手配合做出动态值用户定义变量不光能放静态值还能配合函数生成动态数据。比如测试创建订单时订单号要求唯一没人会手动改可以用${__time(,)}生成时间戳也可以${__Random(1000,9999,)}生成指定范围的随机数。组合写法也很常见比如用户名参数我可以写成${username}_${__Random(1,100,)}这样每次请求都带一个不同的用户名。函数本质上返回的值会赋值给用户定义的变量里的表达式位置运行时再计算所以不用担心自己每次手动改数据。需要注意的是用户定义变量是在测试计划启动时赋值的。如果你在运行中通过某种脚本改了它的值当前计划不会刷新这个特性在涉及登录态刷新时要特别注意。2.3 它的边界在哪里用户定义变量最大的优点是简单、全局、一眼能看懂。但它解决不了两类问题一是值来自上一个请求的响应二是数据成百上千条需要外部维护。这两种场景硬用用户定义变量要么写出来一堆毫无意义的常量要么维护成本剧增。另外补充一点很多新手分不清用户定义的变量和用户参数。简单说用户定义变量作用于整个测试计划而用户参数通常放到线程组或取样器上且支持多组取值常用于并发场景里给不同线程提供不同的数据。普通脚本传参用前者就够了。3. 方法二响应提取器——接口关联的黄金方案3.1 正则表达式提取器的完整配置接口关联最经典的方案就是正则表达式提取器。右键HTTP请求 → 添加 → 后置处理器 → 正则表达式提取器。先看最常用的配置引用名称token正则表达式token:([^])模板$1$匹配数字1缺省值NOT_FOUND这里每个字段都是有讲究的。引用名称是你待会儿在下一个请求里用的变量名填token后面就用${token}取。正则表达式里用括号括起来的部分是真正要提取的内容模板 $1$ 表示取第一个括号匹配到的值。匹配数字填1表示只取第一次匹配到的结果如果响应里有多个token填0是随机取一个填-1是取全部。这个例子面对的场景是响应JSON里的token字段。比如登录接口返回{code:0,data:{token:abc123xyz}}正则表达式就要写成token:([^])。注意我在表达式里把冒号、引号都写进去了这样能准确定位避免匹配到别的同名文本。类似地如果要从响应里取一个订单ID比如orderId:10086就写orderId:(\d)。字符串类型和数字类型的正则写法不同数字要注意用 \d。3.2 JSON提取器比正则更稳的选择如果你用JMeter 4.0以上版本我的建议是优先用JSON提取器。它是专门为JSON响应设计的不需要自己写正则只要会写JSONPath就行。添加路径HTTP请求 → 添加 → 后置处理器 → JSON提取器。配置里写变量名称accessTokenJSONPath表达式$.data.token匹配数字1缺省值TOKEN_NOT_FOUNDJSONPath的规则很好理解$代表整个响应体.data就是取data字段.token就是取data下的token字段。如果要取数组里的第一笔订单号表达式写成$.data.orders[0].orderId即可。为什么我建议优先JSON提取器因为它解析的是JSON结构不是文本匹配后端字段顺序调整或者返回格式有细微变化JSON提取器依然稳定。正则表达式如果写得不够严谨很容易被换行、空格干扰。当然如果你的响应是XML或者纯文本那只能老老实实用正则提取器这类情况在老旧系统接口里仍然大量存在。3.3 提取结果怎么调试很多新手配置完提取器下一个请求还是报错连值有没有提取到都不知道。这时候不用瞎猜用JMeter自带的调试工具排查。最快的办法是加一个调试取样器线程组 → 添加 → 取样器 → Debug Sample。运行后查看结果树里Debug Sample的响应数据会列出所有当前作用域的变量包括你提取的token。如果里面显示tokenabc123xyz说明提取成功如果显示tokenNOT_FOUND说明正则或者JSONPath没匹配上。有个更直接的技巧先用查看结果树看上一个请求的完整响应内容确认里面确实有你要的值再对照检查提取表达式。提取不到的报错绝大多数是表达式和响应格式对不上比如字段名拼错、大小写不一致、引号是中文全角等等。另外推荐一个组合在HTTP请求下加一个JSON断言断言条件写$.data.token不为空。这样提取不到token时脚本会立刻失败方便定位是第几步出的问题而不是在后面的业务请求里绕半天。3.4 多个匹配值和作用域问题响应里有多个同名字段的场景也经常遇到比如一个接口返回了多条记录每条记录都有一个id你想拿所有id做后续批量操作。正则提取器匹配数字填 -1JMeter会生成 id_1、id_2、id_3这样的变量序列。如果你只想要第一个匹配数字填1变量名还是id后面直接用${id}。作用域问题前面说过这里再强调提取器如果放在某个HTTP请求下面它的作用域只对这个请求和这个请求后面的、同样在这个线程组里的请求有效。如果你要把提取结果给另一个线程组用就需要用到后面进阶章节里的跨线程组方法。4. 方法三CSV参数化——批量数据的分发入口4.1 CSV Data Set Config关键配置项接口做批量测试或者压测时需要模拟大量不同用户手写变量肯定不现实。这时候用CSV数据文件最靠谱。配置元件叫CSV Data Set Config添加路径线程组 → 添加 → 配置元件 → CSV数据文件设置。我用表格列一下最需要关注的配置项配置项推荐值说明文件名/path/to/users.csvCSV文件绝对路径或相对bin目录的路径文件编码UTF-8中文内容务必指定不指定容易乱码变量名称username,password按逗号分隔对应CSV每一列分隔符,默认逗号注意文件实际用什么符号是否允许带引号False一般不建议开着开了反而容易引用混乱遇到文件结束符再次循环True循环次数超过数据行数时从头再读遇到文件结束符停止线程False数据用完直接结束线程常用于数据量正好时线程共享模式所有线程各线程是否共享CSV文件指针比如你的 users.csv 里内容是这样test01,123456 test02,abcdef test03,888888变量名称填username,password那么第一列对应${username}第二列对应${password}。请求体里直接引用即可每次迭代会自动读下一行。4.2 每个线程分块取值到底怎么实现最近有个问题被问得很多同一个CSV参数化文件里怎么让每个线程取不同的一段数据比如线程1取第1到10行线程2取第11到20行。这个场景在压测时很常见目的是避免多个线程在同一时间取到同一行导致数据冲突。默认情况下CSV Data Set Config的共享模式选择所有线程文件指针是全局共享的各线程并发时谁先取到哪一行并不固定。要做到线程分块取值有两个思路。思路一把共享模式改为当前线程组然后每个线程组单独配一个CSV文件或者同一个文件的不同起始位置。这种方式配置简单但文件多了维护麻烦。思路二不用CSV Data Set Config改用JSR223取样器或Beanshell脚本按线程号计算行范围自己读取文件。脚本里拿到线程号ctx.getThreadNum()再根据线程数算出每个线程的起始行和结束行用Java的BufferedReader逐行读取。这种方式更灵活适合数据分块逻辑复杂的场景但对脚本能力有要求。如果用JMeter的__CSVRead函数也可以实现分块。${__CSVRead(file, offset)}允许指定文件路径和列偏移量配合__threadNum计算偏移量能控制每个线程从特定行开始读。不过函数写法比较绕我一般只在简单场景用。4.3 CSV传参的中文乱码和经典报错CSV传参的坑十个有七个是编码问题。最常见的是CSV里写了中文JMeter读出来全是乱码传到后台上报错乱码。原因很简单JMeter默认按平台编码读文件中文环境常是GBK但你的Excel或文本编辑器保存的是UTF-8。解决方法是统一编码要么保存CSV时选择UTF-8并在文件编码处明确写 UTF-8要么保存成GBK文件编码留空或改成GBK。我统一推荐UTF-8因为当前后端接口绝大多数是UTF-8。另一个经典报错是CSV里明明有10行数据跑了一遍发现所有请求用的都是第一行数据。这种通常是共享模式选错了或者忘了设置循环次数。线程组里如果循环次数设的是1而线程数小于数据行数CSV只会被读一部分。想完整读完把循环次数设置为永远然后勾选遇到文件结束符停止线程。还有个不起眼但很坑的点CSV文件路径。JMeter的CSV文件路径默认是相对bin目录的如果你把文件放在脚本同目录路径得写对。我踩过一次坑——本地跑得好好的别人拿到脚本后跑不起来就是因为路径写的是绝对路径换机器就失效。后来我习惯把CSV文件和jmx脚本放同一目录文件明里填相对路径或者用${__P(user.dir)}这类函数拼接路径。5. 进阶把数据库查询结果传成下个接口参数5.1 JDBC Request的参数化用法回到热搜词里很关心的那个场景jmeter将jdbc request查询出的数据作为下一个接口的参数。这个需求在真实项目里非常常见比如创建订单前先从订单表中查一条待处理记录把订单号作为创建支付请求的参数。实现步骤分三步。第一步先配置数据库连接测试计划 → 添加 → 配置元件 → JDBC Connection Configuration。关键配置是Database URL、JDBC Driver class和用户名密码。比如MySQL的话URL写jdbc:mysql://192.168.1.100:3306/testdb驱动类写com.mysql.jdbc.Driver注意提前把MySQL驱动jar包放到JMeter的lib目录下并重启JMeter。第二步添加JDBC Request取样器线程组 → 添加 → 取样器 → JDBC Request。填写SQL查询语句比如SELECT order_id FROM orders WHERE status0 ORDER BY create_time LIMIT 1。这里有个重要配置Variable Names。你在这里填一个名字比如orderId查询结果的第一列就会被存成变量${orderId}。如果查询返回多列变量名用逗号分隔比如orderId,status,amount对应每一列。第三步在下一个HTTP请求里引用${orderId}。比如支付接口的请求体写{orderId:${orderId}}运行时JMeter会从数据库查出来的结果里取值填充。这个方案的性能损耗也要说一下。每迭代一次就查一次数据库数据库压力不小。如果数据是只读的且量不大可以在SetUp线程组里先查一次并保存成属性后续线程直接引用避免反复查询。5.2 跨线程组传参__setProperty 的正确打开方式接下来是很多做综合压测的人逃不掉的问题登录接口在SetUp线程组里执行拿到了登录token业务压测在另一个线程组里执行怎么把token传过去前面说过不同线程组的变量是隔离的解决办法是用JMeter属性。属性是全局的可以理解为整个JMeter进程级别的共享变量用__setProperty函数写属性用__P函数读属性。具体步骤在登录请求的正则提取器拿到token变量后加一个BeanShell断言或JSR223断言也可以加一个用户参数写一行${__setProperty(globalToken, ${token},)}这行字的意思是把变量 token 的值写入属性 globalToken。注意函数的前两个参数第一个是属性名第二个是属性值。属性名可以自己起最好和变量区分开。需要用到这个token的线程组里直接用${__P(globalToken,)}引用即可。比如HTTP请求头里填Authorization: Bearer ${__P(globalToken,)}。我自己的习惯是配合SetUp线程组一起用SetUp线程组负责登录并写入属性业务线程组负责压测业务线程组加一个线程组的SetUp动作确保登录完成后再开始压测。这样脚本启动顺序可控属性也一定拿得到。JSR223和BeanShell哪个好我的建议是能用JSR223就别用BeanShell。BeanShell性能差且会引入脚本解释开销而且新版JMeter里BeanShell相关组件逐渐边缘化。JSR223配合Groovy是目前社区主流选择性能好能写复杂逻辑。6. 三种方法怎么选以及我踩过的坑6.1 选型参考表直接给一个总结性的选择参考这是我做项目评审时常用的判断逻辑场景推荐方法理由环境地址、端口、公共账号用户定义变量全局生效方便切换环境登录响应里的token传给后续接口正则/JSON提取器标准接口关联姿势批量账号做数据驱动CSV数据文件数据外部化便于维护从数据库取数作为入参JDBC Request 变量直接复用业务数据跨线程组共享登录态__setProperty __P突破线程组变量隔离动态随机数、时间戳函数助手简单免脚本这里多说一句三种基础方法不是互斥的实际项目里经常是组合使用。比如用户定义变量放环境配置CSV放测试数据提取器负责接口关联三管齐下很正常。6.2 组合实战一个带token的批量查询脚本长什么样以最典型的登录后批量查订单场景为例把整条链路串一遍第一步在用户定义变量里配协议、地址、端口。第二步SetUp线程组里放登录HTTP请求响应里提取accessToken变量然后用__setProperty把它存成全局属性。第三步主线程组加CSV数据文件文件里每行一个订单号变量名称填 orderNo。第四步主线程组的HTTP查询请求请求头里加Authorization: Bearer ${__P(globalToken,)}请求参数里用${orderNo}。第五步设置线程组循环次数和CSV数据量匹配或者勾选EOF停止线程。这套组合既能验证接口功能是否正常也能用来做小规模压测观察吞吐量。脚本的可读性和复用性都不错。6.3 高频问题排查清单最后把参数传递容易踩的坑集中列一下遇到问题可以先对着排查提取出来但引用变量是空先看变量作用域再看提取器放在哪个层级最后用Debug Sampler确认变量名有没有拼错。${变量}原样发送到后台说明JMeter没识别这个变量。检查是不是在请求体里用了错误的引用符号比如写成了$变量或者变量根本没定义。CSV中文乱码文件编码改成UTF-8且文件真的是UTF-8保存的。用记事本另存为时选UTF-8编码。JDBC查询结果拿不到确认驱动jar、URL域名、账号密码没问题用JDBC Request的Variable Names必须填写否则查询结果不会赋值给变量。跨线程组值取不到别用${token}要用${__P(token,)}读属性检查写入属性的脚本是否在读取之前执行。压测时数据重复检查CSV的Recycle on EOF配置如果开了循环且线程数大于数据行数数据必然重复。想数据不重复要么扩大数据量要么勾选Stop thread on EOF。篇幅原因很多东西没法逐个截图演示但你只要对照这篇文章的配置项和示例一步步敲一遍JMeter参数传递这块基本就通了。我以前带新人最快的学习路径也是先用户定义变量再做提取器关联再上CSV参数化最后自己动手做一套登录加业务的完整脚本理论加实操参数传递就再也难不住你。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →