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()
问题原因
- XML命名空间未处理:SOAP响应通常自带命名空间,
XmlSlurper默认不处理命名空间,导致findAll { it.name() == 'name' }无法匹配到目标节点,最终out_filename为空。 - 空值未校验:当找不到
name节点时,toString()返回空字符串,后续substring(1, ...)会抛出异常,间接导致out_filename赋值失败。 - 执行时机差异:手动执行时响应已完全加载且可能无命名空间问题,但自动运行时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
相关产品推荐
相关产品推荐

