You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SoapUI脚本断言保存SOAP响应附件失败:out_filename为空

问题描述

在SoapUI中使用Groovy脚本作为Script Assertion保存SOAP响应附件时,手动执行脚本可成功保存,但运行SOAP请求时失败,报错out_filename is null。使用的脚本如下:

def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
def props = messageExchange.modelItem.testStep.testCase.getTestStepByName("Properties")
def outputDir = new File(context.expand('${Properties#outputFolder}'))

responseContent = context.response
def parsedXML = new XmlSlurper().parseText(responseContent)
def out_filename = parsedXML.'**'.findAll { it.name() == 'name' }.toString();
out_filename = out_filename.substring(1, out_filename.length() - 1);
props.setPropertyValue("out_filename",out_filename);

def fileName = outputDir.toString() +  '\\' + out_filename
def response = messageExchange.response
assert null != response, "response is null"
def outFile = new FileOutputStream(new File(fileName))
def ins = messageExchange.responseAttachments[0]?.inputStream

if (ins) {
com.eviware.soapui.support.Tools.writeAll(outFile, ins)
}
ins.close()
outFile.close()
问题原因
  1. XML命名空间未处理:SOAP响应通常自带命名空间,XmlSlurper默认不处理命名空间,导致findAll { it.name() == 'name' }无法匹配到目标节点,最终out_filename为空。
  2. 空值未校验:当找不到name节点时,toString()返回空字符串,后续substring(1, ...)会抛出异常,间接导致out_filename赋值失败。
  3. 执行时机差异:手动执行时响应已完全加载且可能无命名空间问题,但自动运行时SoapUI的响应处理流程可能导致XML结构或命名空间表现不同。
修复方案

方案1:处理命名空间+空值校验

修改XML解析逻辑,忽略命名空间并添加空值检查,避免异常:

def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
def props = messageExchange.modelItem.testStep.testCase.getTestStepByName("Properties")
def outputDir = new File(context.expand('${Properties#outputFolder}'))

// 确保输出目录存在
if (!outputDir.exists()) {
    outputDir.mkdirs()
}

responseContent = context.response
// 忽略XML命名空间,确保能匹配到节点
def parsedXML = new XmlSlurper().setNamespaceAware(false).parseText(responseContent)
def nameNodes = parsedXML.'**'.findAll { it.name() == 'name' }

// 空值校验:仅找到节点时才赋值
def out_filename = nameNodes ? nameNodes.first().text() : null
assert out_filename != null, "未能从响应中提取文件名"

props.setPropertyValue("out_filename", out_filename)

// 使用File构造函数拼接路径,适配不同系统分隔符
def fileName = new File(outputDir, out_filename).toString()
def response = messageExchange.response
assert response != null, "response is null"
def ins = messageExchange.responseAttachments[0]?.inputStream

def outFile = null
try {
    if (ins) {
        outFile = new FileOutputStream(new File(fileName))
        com.eviware.soapui.support.Tools.writeAll(outFile, ins)
    }
} finally {
    // 确保流资源正确释放
    if (ins) ins.close()
    if (outFile) outFile.close()
}

方案2:直接从附件获取文件名(更可靠)

如果附件本身带有文件名,无需解析XML,直接从附件对象提取:

def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
def props = messageExchange.modelItem.testStep.testCase.getTestStepByName("Properties")
def outputDir = new File(context.expand('${Properties#outputFolder}'))

if (!outputDir.exists()) {
    outputDir.mkdirs()
}

def response = messageExchange.response
assert response != null, "response is null"
def attachment = messageExchange.responseAttachments[0]
assert attachment != null, "未找到响应附件"

// 直接从附件对象获取文件名
def out_filename = attachment.fileName
assert out_filename != null && out_filename.trim() != "", "附件文件名为空"

props.setPropertyValue("out_filename", out_filename)

def fileName = new File(outputDir, out_filename).toString()
def ins = attachment.inputStream

def outFile = null
try {
    outFile = new FileOutputStream(new File(fileName))
    com.eviware.soapui.support.Tools.writeAll(outFile, ins)
} finally {
    if (ins) ins.close()
    if (outFile) outFile.close()
}
关键优化点
  • 命名空间兼容:通过setNamespaceAware(false)让XmlSlurper忽略命名空间,避免节点匹配失败。
  • 提前异常捕获:添加assert语句提前校验空值,避免后续流程因空值崩溃。
  • 跨平台路径:使用File构造函数拼接路径,自动适配Windows/Linux的路径分隔符,避免硬编码\\的问题。
  • 资源安全:将流关闭放在finally块中,确保无论是否发生异常,资源都能正确释放。

内容的提问来源于stack exchange,提问作者plaidshirt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 17:17:46