refactor(接口测试): 优化场景报告

This commit is contained in:
wxg0103 2024-03-28 17:51:42 +08:00 committed by Craftsman
parent c06287bbbc
commit 4cef3121d9
8 changed files with 64 additions and 8 deletions

View File

@ -46,6 +46,8 @@ public class ApiScenarioReportStepDTO {
@Schema(description = "脚本标识")
private String scriptIdentifier;
@Schema(description = "循环控制器步骤的排序")
private Long loopIndex;
@Schema(description = "子节点")
private List<ApiScenarioReportStepDTO> children;

View File

@ -80,7 +80,8 @@
api_scenario_report_detail.request_time,
api_scenario_report_detail.code,
api_scenario_report_detail.response_size,
api_scenario_report_detail.script_identifier
api_scenario_report_detail.script_identifier,
api_scenario_report_detail.sort as loopIndex
from api_scenario_report_step
left join api_scenario_report_detail
on api_scenario_report_step.step_id = api_scenario_report_detail.step_id

View File

@ -47,16 +47,16 @@ public class ResponseHeaderAssertionConverter extends AssertionConverter<MsRespo
}
String expectedValue = msAssertion.getExpectedValue();
String condition = msAssertion.getCondition();
assertion.setName(String.format("Response header %s %s", condition.toLowerCase().replace("_", ""), expectedValue));
MsAssertionCondition msAssertionCondition = EnumValidator.validateEnum(MsAssertionCondition.class, condition);
String header = msAssertion.getHeader();
String testString = switch (msAssertionCondition) {
case CONTAINS -> StringUtils.join("\\b", msAssertion.getHeader(),": .*", expectedValue, ".*\\b");
case NOT_CONTAINS -> StringUtils.join("\\b", msAssertion.getHeader(),": (?!.*", expectedValue, ").*\\b");
case CONTAINS -> StringUtils.join("\\b", header,": .*", expectedValue, ".*\\b");
case NOT_CONTAINS -> StringUtils.join("\\b", header,": (?!.*", expectedValue, ").*\\b");
case EQUALS -> StringUtils.join("\\b", header,": ",expectedValue, "\\b");
case NOT_EQUALS -> StringUtils.join("\\b", msAssertion.getHeader(),": (?!", expectedValue,"\\b)\\d+");
case NOT_EQUALS -> StringUtils.join("\\b", header,": (?!", expectedValue,"\\b)\\d+");
default -> expectedValue;
};
assertion.setName(String.format("Response header %s %s %s", header, condition.toLowerCase().replace("_", ""), expectedValue));
assertion.addTestString(testString);
assertion.setToContainsType();

View File

@ -34,7 +34,7 @@ public class JSONPathAssertionConverter extends ResponseBodyTypeAssertionConvert
String condition = msAssertion.getCondition();
String expression = msAssertion.getExpression();
String expectedValue = msAssertion.getExpectedValue();
assertion.setName(String.format("Response date JSONPath expect %s %s %s", expression, condition.toLowerCase().replace("_", ""), expectedValue));
assertion.setName(String.format("Response data JSONPath expect %s %s %s", expression, condition.toLowerCase().replace("_", ""), expectedValue));
if (BooleanUtils.isFalse(globalEnable)) {
// 如果整体禁用则禁用
assertion.setEnabled(false);

View File

@ -32,7 +32,7 @@ public class RegexAssertionConverter extends ResponseBodyTypeAssertionConverter<
private ResponseAssertion parse2RegexResponseAssertion(MsRegexAssertionItem msAssertion, Boolean globalEnable) {
ResponseAssertion assertion = AssertionConverter.createResponseAssertion();
assertion.setEnabled(msAssertion.getEnable());
assertion.setName("Response date expect regex " + msAssertion.getExpression());
assertion.setName("Response data expect regex " + msAssertion.getExpression());
assertion.addTestString(msAssertion.getExpression());
assertion.setTestFieldResponseData();
if (BooleanUtils.isFalse(globalEnable)) {

View File

@ -46,7 +46,7 @@ public class XPathAssertionConverter extends ResponseBodyTypeAssertionConverter<
assertion.setEnabled(msAssertion.getEnable());
assertion.setTolerant(true);
assertion.setValidating(false);
assertion.setName("Response date expect xpath " + msAssertion.getExpression());
assertion.setName("Response data expect xpath " + msAssertion.getExpression());
assertion.setProperty(TestElement.TEST_CLASS, XPathAssertion.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(XPATH_ASSERTION_GUI));
assertion.setXPathString(msAssertion.getExpression());

View File

@ -68,6 +68,7 @@ public class ApiScenarioReportService {
private EnvironmentGroupMapper environmentGroupMapper;
@Resource
private UserMapper userMapper;
private static final String SPLITTER = "_";
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertApiScenarioReport(List<ApiScenarioReport> reports, List<ApiScenarioRecord> records) {
@ -245,6 +246,27 @@ public class ApiScenarioReportService {
for (ApiScenarioReportStepDTO step : steps) {
List<ApiScenarioReportStepDTO> children = scenarioReportStepMap.get(step.getStepId());
if (CollectionUtils.isNotEmpty(children)) {
//如果是循环控制器 需要重新处理
if (StringUtils.equals(ApiScenarioStepType.LOOP_CONTROLLER.name(), step.getStepType())) {
//根据stepId进行分组
Map<String, List<ApiScenarioReportStepDTO>> loopMap = children.stream().collect(Collectors.groupingBy(ApiScenarioReportStepDTO::getStepId));
List<ApiScenarioReportStepDTO> newChildren = new ArrayList<>();
loopMap.forEach((key, value) -> {
ApiScenarioReportStepDTO loopStep = new ApiScenarioReportStepDTO();
BeanUtils.copyBean(loopStep, value.getFirst());
newChildren.add(loopStep);
value.sort(Comparator.comparingLong(ApiScenarioReportStepDTO::getLoopIndex));
for (int i = 0; i < value.size(); i++) {
ApiScenarioReportStepDTO loop = value.get(i);
loop.setSort((long) i+1);
loop.setParentId(key);
loop.setStepId(loopStep.getStepId() + SPLITTER + loop.getSort());
}
scenarioReportStepMap.put(key, value);
});
children = newChildren;
scenarioReportStepMap.remove(step.getStepId());
}
children.sort(Comparator.comparingLong(ApiScenarioReportStepDTO::getSort));
step.setChildren(children);
getStepTree(children, scenarioReportStepMap);
@ -282,7 +304,19 @@ public class ApiScenarioReportService {
}
public List<ApiScenarioReportDetailDTO> getDetail(String reportId, String stepId) {
//如果是循环控制器下的步骤id 会带着第几条 需要分割处理
String index = null;
if (StringUtils.isNotBlank(stepId) && StringUtils.contains(stepId, SPLITTER)) {
index = StringUtils.substringAfter(stepId, SPLITTER);
stepId = StringUtils.substringBefore(stepId, SPLITTER);
}
List<ApiScenarioReportDetail> apiReportDetails = checkResourceStep(stepId, reportId);
apiReportDetails.sort(Comparator.comparingLong(ApiScenarioReportDetail::getSort));
if (StringUtils.isNotBlank(index)) {
ApiScenarioReportDetail apiScenarioReportDetail = apiReportDetails.get(Integer.parseInt(index) -1);
apiReportDetails = Collections.singletonList(apiScenarioReportDetail);
}
List<ApiScenarioReportDetailDTO> results = new ArrayList<>();
apiReportDetails.forEach(apiReportDetail -> {
ApiScenarioReportDetailDTO apiReportDetailDTO = new ApiScenarioReportDetailDTO();

View File

@ -393,6 +393,25 @@ public class ApiScenarioReportControllerTests extends BaseTest {
List<ApiScenarioReportDTO> data = ApiDataUtils.parseArray(JSON.toJSONString(parseResponse(mvcResult).get("data")), ApiScenarioReportDTO.class);
Assertions.assertNotNull(data);
reports = new ArrayList<>();
for (int i = 0; i < 2; i++) {
ApiScenarioReportDetail apiReportDetail = new ApiScenarioReportDetail();
apiReportDetail.setId("test-report-detail-id-loop" + i);
apiReportDetail.setReportId("test-report-detail-id-loop");
apiReportDetail.setStepId("test-report-detail-id-loop");
apiReportDetail.setStatus("success");
apiReportDetail.setResponseSize(0L);
apiReportDetail.setRequestTime((long) i);
apiReportDetail.setSort((long) i);
apiReportDetail.setContent("{\"resourceId\":\"\",\"stepId\":null,\"threadName\":\"Thread Group\",\"name\":\"HTTP Request1\",\"url\":\"https://www.baidu.com/\",\"requestSize\":195,\"startTime\":1705570589125,\"endTime\":1705570589310,\"error\":1,\"headers\":\"Connection: keep-alive\\nContent-Length: 0\\nContent-Type: application/x-www-form-urlencoded; charset=UTF-8\\nHost: www.baidu.com\\nUser-Agent: Apache-HttpClient/4.5.14 (Java/21)\\n\",\"cookies\":\"\",\"body\":\"POST https://www.baidu.com/\\n\\nPOST data:\\n\\n\\n[no cookies]\\n\",\"status\":\"ERROR\",\"method\":\"POST\",\"assertionTotal\":1,\"passAssertionsTotal\":0,\"subRequestResults\":[],\"responseResult\":{\"responseCode\":\"200\",\"responseMessage\":\"OK\",\"responseTime\":185,\"latency\":180,\"responseSize\":2559,\"headers\":\"HTTP/1.1 200 OK\\nContent-Length: 2443\\nContent-Type: text/html\\nServer: bfe\\nDate: Thu, 18 Jan 2024 09:36:29 GMT\\n\",\"body\":\"<!DOCTYPE html>\\r\\n<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class=\\\"bg s_ipt_wr\\\"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class=\\\"bg s_btn_wr\\\"><input type=submit id=su value=百度一下 class=\\\"bg s_btn\\\" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href=\\\"http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === \\\"\\\" ? \\\"?\\\" : \\\"&\\\")+ \\\"bdorz_come=1\\\")+ '\\\" name=\\\"tj_login\\\" class=\\\"lb\\\">登录</a>');\\r\\n </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style=\\\"display: block;\\\">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a>&nbsp;京ICP证030173号&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>\\r\\n\",\"contentType\":\"text/html\",\"vars\":null,\"imageUrl\":null,\"socketInitTime\":14,\"dnsLookupTime\":0,\"tcpHandshakeTime\":0,\"sslHandshakeTime\":0,\"transferStartTime\":166,\"downloadTime\":5,\"bodySize\":2443,\"headerSize\":116,\"assertions\":[{\"name\":\"JSON Assertion\",\"content\":null,\"script\":null,\"message\":\"Expected to find an object with property ['test'] in path $ but found 'java.lang.String'. This is not a json object according to the JsonProvider: 'com.jayway.jsonpath.spi.json.JsonSmartJsonProvider'.\",\"pass\":false}]},\"isSuccessful\":false,\"fakeErrorMessage\":\"\",\"fakeErrorCode\":null}\n".getBytes());
reports.add(apiReportDetail);
}
apiScenarioReportDetailMapper.batchInsert(reports);
mockMvc.perform(getRequestBuilder(DETAIL + "test-report-detail-id-loop" + "/" + "test-report-detail-id-loop_2"))
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
mockMvc.perform(getRequestBuilder(DETAIL + "test" + "/" + "test"))
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());