JMeter中ForEach控制器未触发及JSON对象取值问题排查
咱们一步步来梳理和解决你遇到的几个问题:
问题1:ForEach控制器未触发的原因
你最开始遇到的No entries found - null first entry: SupportedTags_1错误,核心原因是JMeter的ForEach控制器默认需要带数字索引格式的变量(比如SupportedTags_1、SupportedTags_2),而你通过JMESPath提取数组到SupportedTags后,并没有自动生成这种索引式变量,所以控制器找不到可遍历的条目。你后来改用BeanShell生成这类变量的思路是完全正确的。
问题2:后续请求无法提取JSON对象属性(变量被字面输出)
你用vars.putObject()存储的是com.eclipsesource.json.JsonValue类型的对象,而JMeter的变量引用(比如${TagObject.course})只能直接解析字符串值,无法直接访问Java对象的属性,所以才会把变量名原样输出。这里给你两种可行的解决方案:
方案1:直接拆分属性为单独的字符串变量
修改BeanShell脚本中的循环逻辑,把每个标签的course、subject、topic单独存成带索引的字符串变量:
import com.eclipsesource.json.*; try { String jsonString = prev.getResponseDataAsString(); JsonObject responseAsJsonObject = JsonObject.readFrom(jsonString); String additionalInfoString = responseAsJsonObject.get("additional-info").asString(); JsonObject additionalInfoJsonObject = JsonObject.readFrom(additionalInfoString); JsonArray supportedTagsJsonObject = additionalInfoJsonObject.get("supported-tags").asArray(); for (int i = 0; i < supportedTagsJsonObject.size(); i++) { JsonObject tagObj = supportedTagsJsonObject.get(i).asObject(); // 提取每个属性并存储为带索引的变量 vars.put("Tag_course_" + (i+1), tagObj.get("course").asString()); vars.put("Tag_subject_" + (i+1), tagObj.get("subject").asString()); vars.put("Tag_topic_" + (i+1), tagObj.get("topic").asString()); } } catch (Exception e){ log.info("error in processing beanshell script: ", e); prev.setSuccessful(false); }
然后配置ForEach控制器:
- 输入变量前缀:填写
Tag_course(或者你想遍历的任意属性前缀) - 输出变量名称:比如
CurrentCourse
如果想在一个循环里同时获取三个属性,可以用JMeter的__V()函数组合索引,比如:
"tags":["existing/${__V(Tag_course_${__jm__ForEach Controller__idx})}-${__V(Tag_subject_${__jm__ForEach Controller__idx})}-${__V(Tag_topic_${__jm__ForEach Controller__idx})}"]
其中${__jm__ForEach Controller__idx}是ForEach控制器自动生成的当前循环索引。
方案2:预生成完整的标签组合字符串
如果你只需要最终的组合值,直接在BeanShell里拼接好存储会更简单:
// 其他代码和之前一致 for (int i = 0; i < supportedTagsJsonObject.size(); i++) { JsonObject tagObj = supportedTagsJsonObject.get(i).asObject(); String fullTag = "existing/" + tagObj.get("course").asString() + "-" + tagObj.get("subject").asString() + "-" + tagObj.get("topic").asString(); vars.put("SupportedTags_" + (i+1), fullTag); }
然后ForEach控制器配置:
- 输入变量前缀:
SupportedTags - 输出变量名称:
CurrentFullTag
后续请求里直接使用:
"tags":["${CurrentFullTag}"]
额外建议:用JSR223(Groovy)替代BeanShell
Groovy性能比BeanShell好很多,而且处理JSON更简洁,推荐换成JSR223 Sampler,脚本如下:
import groovy.json.JsonSlurper def jsonSlurper = new JsonSlurper() def response = jsonSlurper.parseText(prev.getResponseDataAsString()) def additionalInfo = jsonSlurper.parseText(response['additional-info']) def supportedTags = additionalInfo['supported-tags'] supportedTags.eachWithIndex { tag, idx -> def index = idx + 1 // 方式1:拆分属性 vars.put("Tag_course_${index}", tag.course) vars.put("Tag_subject_${index}", tag.subject) vars.put("Tag_topic_${index}", tag.topic) // 方式2:直接存组合值 vars.put("SupportedTags_${index}", "existing/${tag.course}-${tag.subject}-${tag.topic}") }
如何打印JMESPath提取的变量值
有三种简单的方式:
- Debug Sampler:添加一个Debug Sampler到测试计划中,勾选「JMeter Variables」选项,运行后查看结果树里的Debug Sampler输出,就能看到所有变量的名称和对应值。
- 日志打印:在JSR223/BeanShell脚本里加入日志语句,比如:
运行后打开JMeter的log.info("JMESPath提取的AdditionalInfo值:" + vars.get("AdditionalInfoContainingGetSupportedTags"));jmeter.log文件就能看到输出。 - 临时请求验证:在测试计划里加一个临时的HTTP Request(或者Dummy Sampler),把变量引用写在请求内容里,运行后查看结果树的请求详情,就能看到变量是否被正确解析。
内容的提问来源于stack exchange,提问作者Manu Chadha

