JMeter是大家常用的性能测试工具之一,不知道你有没有遇到过这样的情况:在写JMeter脚本的时候,需要在不同线程组之间传递变量。这看似是个小问题,但要是处理不好,会给整个测试过程带来不少麻烦。今天咱们就来好好聊聊,在JMeter脚本里,如何实现多个线程组之间的变量传递。

一、为啥要在线程组之间传递变量?

在JMeter里,每个线程组通常用来模拟不同用户的行为。不过,实际测试中,我们经常会碰到需要把一个线程组产生的数据,用到另一个线程组里的情况。

比如说,在测试一个电商平台时,第一个线程组模拟用户登录,登录成功后会生成用户登录信息,像用户ID、token这些数据。而后续的第二个线程组可能要模拟用户下单,下单操作就需要用到之前登录生成的用户ID,来确认是哪个用户在进行操作。

又或者在测试某个API接口时,第一个线程组先调用接口获取一个动态变化的值,比如商品的库存数量。第二个线程组在执行其他操作,像是模拟购买商品时,就需要用到这个库存数量,看看购买操作是否能成功。要是没办法在不同线程组之间传递变量,数据共享就成了大难题,测试的难度也会直线上升。所以,学会在JMeter里实现线程组间的变量传递,对做好复杂的性能测试至关重要。

二、Property实现变量传递

在JMeter里,变量分为局部变量和全局变量。普通的变量(Variable)是局部的,只能在它所在的线程组里使用;而Property是全局的,能在整个脚本的多个线程组之间共享。因此,利用Property来传递变量是很常用的方法。

1. 设置全局变量(Property)

想要设置全局变量,我们可以借助JMeter里的Beanshell或者JSR223元件。这里以JSR223 PreProcessor为例,给大家详细讲讲。

//获取本地变量值 String path = vars.get("path1"); log.info("path1 = " + path); // 在第一个线程组中设置Property变量 props.put("globalPath", path); log.info("globalPath = " + props.get("globalPath")); // 将局部变量值存储为全局Property 

这段代码的意思是:

  • vars.get("path1") 用来获取当前线程组里名为path1的局部变量的值。
  • props.put("globalPath", path) 是把刚刚获取到的局部变量path1的值,设置成一个名为globalPath的全局变量。这样一来,其他线程组就能访问这个globalPath变量了。

可能有的小伙伴会问,这里获取的本地变量path1要怎么配置呢?其实配置本地变量的方法有好几种,下面给大家介绍一种比较简单的。在JMeter里的“User Defined Variables”(用户定义变量)中,设置一个名为path1的变量,把它的值设为index就行,就像下面这样:

NameValueDescription
path1index

2. 获取全局变量(Property)

在第二个线程组里,获取之前设置的全局变量也不难,代码如下:

// 在第二个线程组中获取全局Property变量
 String globalPath = props.get("globalPath"); // 从全局Property中获取变量
 log.info("Global path: " + props.get("globalPath"));

 vars.put("path2", globalPath); // 将全局变量的值赋给本线程组的局部变量 

这段代码里:

  • props.get("globalPath") 负责从全局的Property里把globalPath这个变量的值取出来。
  • vars.put("path2", globalPath) 是把取出来的全局变量的值,赋给当前线程组里的局部变量path2。这样,后续在这个线程组里的请求,就可以使用path2这个变量的值了。

3. 传递变量的完整步骤

具体操作的时候,可以按照下面的步骤来:

  • 第一步:在第一个线程组里,用JSR223元件(比如JSR223预处理器)设置一个局部变量,然后把这个局部变量的值存储为全局的Property变量。
  • 第二步:在第二个线程组里,同样使用JSR223元件,从全局的Property变量里获取之前设置的值,再把它传递给当前线程组的局部变量。
  • 第三步:验证变量传递的效果。在第二个线程组里,可以通过打印日志,看看变量的值是否正确;也可以添加断言,检查使用这个变量的请求是否成功,以此来确认变量是否成功传递并被正确使用。

给大家举个实际的例子,假设要测试一个电商网站,第一个线程组模拟用户登录,生成用户ID;第二个线程组用这个用户ID去查询用户的订单信息。

  • 第一个线程组的JSR223 PreProcessor代码
String userId = "12345"; // 模拟登录后生成的用户ID vars.put("userId", userId); // 设置局部变量 props.put("globalUserId", userId); // 设置全局Property变量 
  • 第二个线程组的JSR223 PreProcessor代码
String globalUserId = props.get("globalUserId"); // 获取全局变量 vars.put("userId", globalUserId); // 设置局部变量,供后续请求使用 
  • 第二个线程组的HTTP请求:在HTTP请求里,通过${userId}这样的方式,就能引用刚刚设置的局部变量,去完成查询用户订单的请求啦。

三、借助文件或数据库共享数据

除了用Property变量,我们还能通过文件或者数据库来实现数据共享,下面给大家介绍两种常见的做法。

1. 使用CSV数据文件共享变量

  • 写入CSV文件:在第一个线程组里,利用JSR223或BeanShell元件,把想要传递的变量写入CSV文件。比如下面这段代码,就能把userId变量的值写入user_data.csv文件里:
def writer = new FileWriter("user_data.csv", true); writer.append(vars.get("userId") + "n"); writer.flush(); writer.close(); 
  • 读取CSV文件:到了第二个线程组,使用CSV Data Set Config来读取这个CSV文件里的变量。这里要特别注意,一定要把CSV文件的路径配置正确,不然就读取不到数据了。

2. 使用数据库共享变量

  • 写入数据库:在第一个线程组里,可以通过JDBC请求或者JSR223元件,把变量插入到数据库里。比如说下面这段代码,就能把userId变量插入到名为users的数据库表中:
// 示例:通过JDBC插入数据 String insertQuery = "INSERT INTO users (userId) VALUES ('" + vars.get("userId") + "')"; java.sql.Statement stmt = conn.createStatement(); stmt.executeUpdate(insertQuery); 
  • 读取数据库:在第二个线程组里,还是通过JDBC请求从数据库里读取变量。下面这段代码就是从users表中读取userId变量的示例:
// 示例:通过JDBC读取数据 String query = "SELECT userId FROM users WHERE id = 1"; java.sql.ResultSet rs = stmt.executeQuery(query); while (rs.next()) { vars.put("userId", rs.getString("userId")); } 

四、调试和验证很重要

在传递变量的过程中,为了能及时发现问题,调试是必不可少的。我们可以在JSR223元件里添加日志输出代码,像下面这样:

log.info("Global UserId: " + props.get("globalUserId")); 

通过查看日志输出的内容,就能很清楚地知道变量有没有正确传递。要是发现变量的值不对,或者根本没传递成功,就可以顺着日志去排查问题出在哪里。

五、及时沟通测试结果

在做性能测试的时候,和开发团队、产品团队保持良好的沟通非常关键。特别是在测试脚本涉及到跨线程组变量传递这种稍微复杂一点的设置时,一定要让团队成员都清楚了解测试脚本的具体情况。

要是在测试过程中发现了问题,我们可以借助JMeter生成的测试报告,或者查看详细的日志文件,把变量传递的过程清晰地展示出来。这样一来,团队成员就能快速定位到问题所在,一起商量解决办法,让测试工作更顺利地进行。

在JMeter中实现线程组之间的变量传递,方法其实有不少。不管是用Property变量,还是通过文件、数据库共享数据,只要掌握了这些技巧,以后在做复杂的性能测试时,设计测试脚本就会轻松很多,测试的准确性和可维护性也能大大提高。对测试新人来说,可以看看《测试人的Python工具书》《性能测试JMeter实战》这些书籍,里面有很多实用的知识,能帮助大家更快上手。希望今天分享的内容对大家有所帮助,要是在实际操作中有什么问题,欢迎在评论区留言交流!