fix(接口测试): 接口用例和场景转性能测试时不再添加误报断言

--bug=1010586 --user=宋天阳
【接口测试】github#10919,如果开启了误报库功能,接口自动化转性能测试会带有误报断言,压测的时候导致大量错误
https://www.tapd.cn/55049933/s/1109862
This commit is contained in:
song-tianyang 2022-02-24 16:57:08 +08:00 committed by xiaomeinvG
parent 7d8b065754
commit 9a318b74cf
6 changed files with 106 additions and 48 deletions

View File

@ -234,7 +234,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
//根据配置增加全局前后至脚本
JMeterScriptUtil.setScriptByHttpConfig(httpConfig, httpSamplerTree, config, useEnvironment, this.getEnvironmentId(), false);
//增加误报断言
if (CollectionUtils.isNotEmpty(httpConfig.getErrorReportAssertions())) {
if (!config.isOperating() && CollectionUtils.isNotEmpty(httpConfig.getErrorReportAssertions())) {
for (MsAssertions assertion : httpConfig.getErrorReportAssertions()) {
assertion.toHashTree(httpSamplerTree, assertion.getHashTree(), config);
}
@ -674,22 +674,22 @@ public class MsHTTPSamplerProxy extends MsTestElement {
list.stream().
filter(KeyValue::isValid).
filter(KeyValue::isEnable).forEach(keyValue -> {
try {
String value = StringUtils.isNotEmpty(keyValue.getValue()) && keyValue.getValue().startsWith("@") ? ScriptEngineUtils.buildFunctionCallString(keyValue.getValue()) : keyValue.getValue();
HTTPArgument httpArgument = new HTTPArgument(keyValue.getName(), value);
if (keyValue.getValue() == null) {
httpArgument.setValue("");
}
httpArgument.setAlwaysEncoded(keyValue.isUrlEncode());
if (StringUtils.isNotBlank(keyValue.getContentType())) {
httpArgument.setContentType(keyValue.getContentType());
}
arguments.addArgument(httpArgument);
} catch (Exception e) {
try {
String value = StringUtils.isNotEmpty(keyValue.getValue()) && keyValue.getValue().startsWith("@") ? ScriptEngineUtils.buildFunctionCallString(keyValue.getValue()) : keyValue.getValue();
HTTPArgument httpArgument = new HTTPArgument(keyValue.getName(), value);
if (keyValue.getValue() == null) {
httpArgument.setValue("");
}
httpArgument.setAlwaysEncoded(keyValue.isUrlEncode());
if (StringUtils.isNotBlank(keyValue.getContentType())) {
httpArgument.setContentType(keyValue.getContentType());
}
arguments.addArgument(httpArgument);
} catch (Exception e) {
}
}
);
}
}
);
return arguments;
}

View File

@ -105,7 +105,7 @@ public class MockApiUtils {
} else if (StringUtils.equalsIgnoreCase(type, "XML")) {
if (bodyObj.containsKey("raw")) {
String xmlStr = bodyObj.getString("raw");
JSONObject matchObj = XMLUtils.XmlToJson(xmlStr);
JSONObject matchObj = XMLUtils.stringToJSONObject(xmlStr);
returnJson = matchObj;
}
} else if (StringUtils.equalsIgnoreCase(type, "Raw")) {
@ -235,7 +235,7 @@ public class MockApiUtils {
if (StringUtils.isNotEmpty(response)) {
try {
JSONObject respObj = JSONObject.parseObject(response);
if(respObj != null){
if (respObj != null) {
if (respObj.containsKey("body")) {
String returnStr = "";
JSONObject bodyObj = respObj.getJSONObject("body");
@ -311,15 +311,15 @@ public class MockApiUtils {
}
responseDTO.setReturnCode(code);
}
if(respObj.containsKey("headers")){
if (respObj.containsKey("headers")) {
JSONArray jsonArray = respObj.getJSONArray("headers");
Map<String,String> headMap = new HashMap<>();
for(int i = 0; i < jsonArray.size(); i ++){
Map<String, String> headMap = new HashMap<>();
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject headObj = jsonArray.getJSONObject(i);
if(headObj.containsKey("name") && headObj.containsKey("value") && headObj.containsKey("enable")){
if (headObj.containsKey("name") && headObj.containsKey("value") && headObj.containsKey("enable")) {
boolean enable = headObj.getBoolean("enable");
if(enable){
headMap.put(headObj.getString("name"),headObj.getString("value"));
if (enable) {
headMap.put(headObj.getString("name"), headObj.getString("value"));
}
}
}
@ -482,7 +482,7 @@ public class MockApiUtils {
return returnJson;
} else if (StringUtils.startsWithIgnoreCase(request.getContentType(), "text/xml")) {
String xmlString = readXml(request);
JSONObject object = XMLUtils.XmlToJson(xmlString);
JSONObject object = XMLUtils.stringToJSONObject(xmlString);
return object;
} else if (StringUtils.startsWithIgnoreCase(request.getContentType(), "application/x-www-form-urlencoded")) {
JSONObject object = new JSONObject();

View File

@ -244,7 +244,7 @@ public class HashTreeUtil {
if (envConfig == null) {
return;
}
if (envConfig.isUseErrorCode()) {
if (!config.isOperating() && envConfig.isUseErrorCode()) {
List<MsAssertions> errorReportAssertion = HashTreeUtil.getErrorReportByProjectId(projectId);
for (MsAssertions assertion : errorReportAssertion) {
assertion.toHashTree(samplerHashTree, assertion.getHashTree(), config);

View File

@ -2,15 +2,20 @@ package io.metersphere.commons.utils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.performance.parse.EngineSourceParserFactory;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.regex.*;
public class XMLUtils {
public static void setExpandEntityReferencesFalse(DocumentBuilderFactory documentBuilderFactory){
public static void setExpandEntityReferencesFalse(DocumentBuilderFactory documentBuilderFactory) {
try {
String FEATURE = null;
FEATURE = "http://javax.xml.XMLConstants/feature/secure-processing";
@ -25,10 +30,11 @@ public class XMLUtils {
documentBuilderFactory.setFeature(FEATURE, false);
documentBuilderFactory.setXIncludeAware(false);
documentBuilderFactory.setExpandEntityReferences(false);
}catch (Exception e){
} catch (Exception e) {
LogUtil.error(e);
}
}
private static void jsonToXmlStr(JSONObject jObj, StringBuffer buffer, StringBuffer tab) {
Set<Map.Entry<String, Object>> se = jObj.entrySet();
StringBuffer nowTab = new StringBuffer(tab.toString());
@ -70,7 +76,7 @@ public class XMLUtils {
// 传入完整的 xml 文本转换成 json 对象
public static JSONObject XmlToJson(String xml) {
JSONObject result = new JSONObject();
if(xml == null)
if (xml == null)
return null;
List<String> list = preProcessXml(xml);
try {
@ -85,8 +91,8 @@ public class XMLUtils {
// 预处理 xml 文本转换成 tag + data 的列表
private static List<String> preProcessXml(String xml) {
int begin = xml.indexOf("?>");
if(begin != -1) {
if(begin + 2 >= xml.length()) {
if (begin != -1) {
if (begin + 2 >= xml.length()) {
return null;
}
xml = xml.substring(begin + 2);
@ -104,24 +110,24 @@ public class XMLUtils {
// 传入预处理的列表返回转换成功的 json 对象
private static Object XmlTagToJsonObject(List<String> list) {
if(list == null || list.size() == 0)
if (list == null || list.size() == 0)
return null;
Stack<String> tagStack = new Stack<>(); // tag
Stack<Object> valueStack = new Stack<>(); // 数据栈
valueStack.push(new JSONObject()); // 最终结果将存放在第一个入栈的元素中
for(String item : list) {
for (String item : list) {
String beginTag = isBeginTag(item), endTag = isEndTag(item); // 判断当前 tag 是开始还是结尾
if(beginTag != null) {
if (beginTag != null) {
tagStack.push(beginTag);
valueStack.push(new JSONObject());
} else if(endTag != null) {
if(endTag.equals(tagStack.peek())) { // 是一对 tag
} else if (endTag != null) {
if (endTag.equals(tagStack.peek())) { // 是一对 tag
Object topValue = valueStack.peek();
if(topValue instanceof String) { // 栈顶是纯数据 xml 节点
if (topValue instanceof String) { // 栈顶是纯数据 xml 节点
valueStack.pop();
}
valueStack.pop();
if(valueStack.peek() instanceof JSONObject) {
if (valueStack.peek() instanceof JSONObject) {
((JSONObject) valueStack.peek()).put(tagStack.peek(), topValue);
}
tagStack.pop();
@ -130,7 +136,7 @@ public class XMLUtils {
valueStack.push(item);
}
}
if(valueStack.empty())
if (valueStack.empty())
return null;
return valueStack.peek();
}
@ -155,4 +161,44 @@ public class XMLUtils {
return null;
}
public static Document stringToDocument(String xml) {
try {
return EngineSourceParserFactory.getDocument(new ByteArrayInputStream(xml.getBytes("utf-8")));
} catch (Exception e) {
LogUtil.error(e);
return null;
}
}
public static JSONObject stringToJSONObject(String xml) {
try {
return elementToJSONObject(stringToDocument(xml).getRootElement());
} catch (Exception e) {
LogUtil.error(e);
return null;
}
}
public static JSONObject elementToJSONObject(Element node) {
JSONObject result = new JSONObject();
// 当前节点的名称文本内容和属性
List<Attribute> listAttr = node.attributes();// 当前节点的所有属性的list
for (Attribute attr : listAttr) {// 遍历当前节点的所有属性
result.put(attr.getName(), attr.getValue());
}
// 递归遍历当前节点所有的子节点
List<Element> listElement = node.elements();// 所有一级子节点的list
if (!listElement.isEmpty()) {
for (Element e : listElement) {// 遍历所有一级子节点
if (e.attributes().isEmpty() && e.elements().isEmpty()) // 判断一级节点是否有属性和子节点
result.put(e.getName(), e.getTextTrim());// 沒有则将当前节点作为上级节点的属性对待
else {
if (!result.containsKey(e.getName())) // 判断父节点是否存在该一级节点名称的属性
result.put(e.getName(), new JSONArray());// 没有则创建
((JSONArray) result.get(e.getName())).add(elementToJSONObject(e));// 将该一级节点放入该节点名称的属性对应的值中
}
}
}
return result;
}
}

View File

@ -15,11 +15,11 @@
</el-tooltip>
</el-col>
<el-col :span="3">
<el-tooltip effect="dark" v-if="errorCode" :content="errorCode"
<el-tooltip effect="dark" v-if="baseErrorCode && baseErrorCode!==''" :content="baseErrorCode"
style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" placement="bottom"
:open-delay="800">
<div style="color: #F6972A">
{{ errorCode }}
{{ baseErrorCode }}
</div>
</el-tooltip>
</el-col>
@ -57,7 +57,7 @@
$t('api_test.home_page.detail_card.unexecute')
}}
</el-tag>
<el-tag v-else-if="errorCode" class="ms-test-error_code" size="mini">
<el-tag v-else-if="baseErrorCode && baseErrorCode!== ''" class="ms-test-error_code" size="mini">
{{ $t('error_report_library.option.name') }}
</el-tag>
<el-tag size="mini" type="success" v-else-if="request.success"> {{ $t('api_report.success') }}</el-tag>
@ -102,7 +102,10 @@ export default {
scenarioName: String,
indexNumber: Number,
console: String,
errorCode: String,
errorCode: {
type: String,
default: ""
},
isActive: {
type: Boolean,
default: false
@ -110,6 +113,7 @@ export default {
},
created() {
this.showActive = this.isActive;
this.baseErrorCode = this.errorCode;
},
data() {
return {
@ -120,6 +124,7 @@ export default {
return "#B8741A";
}
},
baseErrorCode: "",
backgroundColor: {
type: String,
default() {
@ -133,14 +138,17 @@ export default {
isActive() {
this.showActive = this.isActive;
},
errorCode() {
this.baseErrorCode = this.errorCode;
},
request: {
deep: true,
handler(n) {
if (this.request.errorCode) {
this.errorCode = this.request.errorCode;
this.baseErrorCode = this.request.errorCode;
} else if (this.request.attachInfoMap && this.request.attachInfoMap.errorReportResult) {
if (this.request.attachInfoMap.errorReportResult !== "") {
this.errorCode = this.request.attachInfoMap.errorReportResult;
this.baseErrorCode = this.request.attachInfoMap.errorReportResult;
}
}
},

View File

@ -44,7 +44,8 @@
right-content="CASE"
>
<template v-slot:version>
<version-select v-xpack :project-id="projectId" :version-id="trashVersion" @changeVersion="changeVersion" />
<version-select v-xpack :project-id="projectId" :version-id="trashVersion"
@changeVersion="changeVersion"/>
</template>
<!-- 列表集合 -->
<ms-api-list
@ -107,7 +108,7 @@
:right-button-enable="currentProtocol === 'HTTP' "
>
<template v-slot:version>
<version-select v-xpack :project-id="projectId" @changeVersion="changeVersion" />
<version-select v-xpack :project-id="projectId" @changeVersion="changeVersion"/>
</template>
<!-- 列表集合 -->
<ms-api-list
@ -360,7 +361,7 @@ export default {
useEnvironment: String,
activeTab: "api",
currentVersion: null,
trashVersion:null,
trashVersion: null,
};
},
activated() {
@ -763,6 +764,9 @@ export default {
if (this.$refs.trashCaseList) {
this.$refs.trashCaseList.initTable();
}
if (this.$refs.nodeTree) {
this.$refs.nodeTree.list();
}
if (this.$refs.apiDefList && this.$refs.apiDefList[0]) {
this.$refs.apiDefList[0].initTable();
}