refactor(接口测试): 优化较多场景步骤加载性能

【【接口测试】场景用例中有500+个请求,编辑失败-页面一直转圈,然后提示504 timeout了】https://www.tapd.cn/55049933/bugtrace/bugs/view?bug_id=1155049933001025950

Signed-off-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
fit2-zhao 2023-05-05 17:42:23 +08:00 committed by 建国
parent c43861ba43
commit 1f09f13d00
10 changed files with 183 additions and 51 deletions

View File

@ -374,22 +374,7 @@ public class ElementUtil {
}
}
} else if (element != null && element.get(PropertyConstant.TYPE).toString().equals(ElementConstants.HTTP_SAMPLER)) {
MsHTTPSamplerProxy httpSamplerProxy = JSON.parseObject(element.toString(), MsHTTPSamplerProxy.class);
if (httpSamplerProxy != null && (!httpSamplerProxy.isCustomizeReq() || (httpSamplerProxy.isCustomizeReq() && BooleanUtils.isTrue(httpSamplerProxy.getIsRefEnvironment())))) {
if (element != null && element.has(ElementConstants.HASH_TREE)) {
httpSamplerProxy.setHashTree(JSONUtil.readValue(element.optString(ElementConstants.HASH_TREE)));
}
HashTree tmpHashTree = new HashTree();
httpSamplerProxy.toHashTree(tmpHashTree, null, msParameter);
if (tmpHashTree != null && tmpHashTree.getArray().length > 0) {
HTTPSamplerProxy object = (HTTPSamplerProxy) tmpHashTree.getArray()[0];
// 清空Domain
element.put("domain", "");
if (object != null && StringUtils.isNotEmpty(object.getDomain())) {
element.put("domain", StringUtils.isNotEmpty(object.getProtocol()) ? object.getProtocol() + "://" + object.getDomain() : object.getDomain());
}
}
}
setDomain(element, msParameter);
}
if (element.has(ElementConstants.HASH_TREE)) {
JSONArray elementJSONArray = element.optJSONArray(ElementConstants.HASH_TREE);
@ -405,6 +390,28 @@ public class ElementUtil {
}
}
public static void setDomain(JSONObject element, MsParameter msParameter) {
MsHTTPSamplerProxy httpSamplerProxy = JSON.parseObject(element.toString(), MsHTTPSamplerProxy.class);
if (httpSamplerProxy != null &&
(!httpSamplerProxy.isCustomizeReq() || (httpSamplerProxy.isCustomizeReq()
&& BooleanUtils.isTrue(httpSamplerProxy.getIsRefEnvironment())))) {
if (element != null && element.has(ElementConstants.HASH_TREE)) {
httpSamplerProxy.setHashTree(JSONUtil.readValue(element.optString(ElementConstants.HASH_TREE)));
}
HashTree tmpHashTree = new HashTree();
httpSamplerProxy.toHashTree(tmpHashTree, null, msParameter);
if (tmpHashTree != null && tmpHashTree.getArray().length > 0) {
HTTPSamplerProxy object = (HTTPSamplerProxy) tmpHashTree.getArray()[0];
// 清空Domain
element.put("domain", "");
if (object != null && StringUtils.isNotEmpty(object.getDomain())) {
element.put("domain", StringUtils.isNotEmpty(object.getProtocol()) ?
object.getProtocol() + "://" + object.getDomain() : object.getDomain());
}
}
}
}
public static void mergeHashTree(MsTestElement element, LinkedList<MsTestElement> targetHashTree) {
try {
if (CollectionUtils.isNotEmpty(element.getHashTree())
@ -1033,7 +1040,7 @@ public class ElementUtil {
}
public static boolean isLoop(MsTestElement sampler) {
if (sampler != null ) {
if (sampler != null) {
if (StringUtils.equals(sampler.getType(), "LoopController")) {
return true;
}

View File

@ -60,6 +60,8 @@ public interface ExtApiScenarioMapper {
ApiScenarioDTO selectById(@Param("id") String id);
List<ApiScenarioDTO> selectByScenarioIds(@Param("ids") List<String> ids);
void clearLatestVersion(String refId);
void addLatestVersion(String refId);

View File

@ -746,6 +746,18 @@
WHERE api_scenario.id = #{id}
</select>
<select id="selectByScenarioIds" resultMap="BaseResultMap">
select api_scenario.*, project_version.name as version_name, project.version_enable
from api_scenario
LEFT JOIN project_version
on project_version.project_id = api_scenario.project_id and project_version.id = version_id
LEFT JOIN project on api_scenario.project_id = project.id
WHERE api_scenario.id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
<sql id="queryVersionCondition">
<if test="request.versionId != null">
and ${versionTable}.version_id = #{request.versionId}

View File

@ -112,4 +112,6 @@ public interface ExtApiTestCaseMapper {
long countSourceIdByProjectIdIsNull();
String findPassRateById(String id);
List<ApiTestCaseInfo> selectByCaseIds(List<String> ids);
}

View File

@ -339,6 +339,22 @@
WHERE t1.id = #{0}
</select>
<select id="selectByCaseIds" resultType="io.metersphere.api.dto.definition.ApiTestCaseInfo">
SELECT t1.*,
a.method AS apiMethod,
project_version.name as version_name,
project.version_enable
FROM api_test_case t1
inner join api_definition a on t1.api_definition_id = a.id
LEFT JOIN project_version
on a.project_id = project_version.project_id AND project_version.id = t1.version_id
LEFT JOIN project on a.project_id = project.id
WHERE t1.id in
<foreach collection="ids" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</select>
<select id="list" resultType="io.metersphere.api.dto.definition.ApiTestCaseResult">
SELECT
t1.*,

View File

@ -206,7 +206,7 @@ public class ApiScenarioController {
@PostMapping("/get-scenario-list")
public List<ApiScenarioDTO> getApiScenarios(@RequestBody List<String> ids) {
return apiAutomationService.getNewApiScenarios(ids);
return apiAutomationService.getScenarioDetail(ids);
}
@PostMapping(value = "/run/debug")

View File

@ -4,7 +4,9 @@ import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.dto.definition.ApiTestCaseInfo;
import io.metersphere.api.dto.definition.request.ElementUtil;
import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import io.metersphere.base.domain.ApiTestCase;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import io.metersphere.base.domain.Project;
import io.metersphere.base.mapper.ApiScenarioMapper;
@ -29,6 +31,7 @@ import org.springframework.stereotype.Service;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class MsHashTreeService {
@ -136,18 +139,23 @@ public class MsHashTreeService {
}
}
private void setElement(JSONObject element, Integer num, Boolean enable, String versionName, Boolean versionEnable) {
private void setElement(JSONObject element, Integer num,
Boolean enable, String versionName,
Boolean versionEnable, ParameterConfig msParameter) {
element.put(NUM, num);
element.put(ENABLE, enable == null ? false : enable);
element.put(VERSION_NAME, versionName);
element.put(VERSION_ENABLE, versionEnable == null ? false : versionEnable);
if (msParameter != null) {
ElementUtil.setDomain(element, msParameter);
}
}
private JSONObject setRequest(JSONObject element) {
private JSONObject setRequest(JSONObject element, Map<String, ApiTestCaseInfo> caseMap, ParameterConfig msParameter) {
boolean enable = element.optBoolean(ENABLE);
boolean isExist = false;
if (StringUtils.equalsIgnoreCase(element.optString(REF_TYPE), CASE)) {
ApiTestCaseInfo apiTestCase = apiTestCaseService.get(element.optString(ID));
ApiTestCaseInfo apiTestCase = caseMap.get(element.optString(ID));
if (apiTestCase != null) {
if (StringUtils.equalsIgnoreCase(element.optString(REFERENCED), REF)) {
JSONObject refElement = JSONUtil.parseObject(apiTestCase.getRequest());
@ -202,7 +210,7 @@ public class MsHashTreeService {
}
element.put(ID, apiTestCase.getId());
isExist = true;
this.setElement(element, apiTestCase.getNum(), enable, apiTestCase.getVersionName(), apiTestCase.getVersionEnable());
this.setElement(element, apiTestCase.getNum(), enable, apiTestCase.getVersionName(), apiTestCase.getVersionEnable(), msParameter);
}
} else if (StringUtils.equalsIgnoreCase(element.optString(REFERENCED), COPY)) {
ApiDefinitionResult definition = apiDefinitionService.getById(element.optString(ID));
@ -210,7 +218,8 @@ public class MsHashTreeService {
Project project = projectMapper.selectByPrimaryKey(definition.getProjectId());
element.put(ID, definition.getId());
element.put(VERSION_ID, definition.getVersionId());
this.setElement(element, definition.getNum(), enable, definition.getVersionName(), project.getVersionEnable());
this.setElement(element, definition.getNum(), enable,
definition.getVersionName(), project.getVersionEnable(), msParameter);
isExist = true;
}
}
@ -223,6 +232,13 @@ public class MsHashTreeService {
return element;
}
private void getCaseIds(JSONObject element, List<String> caseIds) {
if (StringUtils.equalsIgnoreCase(element.optString(REF_TYPE), CASE) && element.has(ID)) {
caseIds.add(element.optString(ID));
}
}
private JSONObject setRefScenario(JSONObject element) {
boolean enable = element.has(ENABLE) ? element.optBoolean(ENABLE) : true;
if (!element.has(MIX_ENABLE)) {
@ -256,7 +272,8 @@ public class MsHashTreeService {
ProjectConfig projectApplication = baseProjectApplicationService.getSpecificTypeValue(scenarioWithBLOBs.getProjectId(), "SCENARIO_CUSTOM_NUM");
element.put(SHOW_CUSTOM_NUM, projectApplication.getScenarioCustomNum());
element.put(CUSTOM_NUM, scenarioWithBLOBs.getCustomNum());
this.setElement(element, scenarioWithBLOBs.getNum(), enable, scenarioWithBLOBs.getVersionName(), scenarioWithBLOBs.getVersionEnable());
this.setElement(element, scenarioWithBLOBs.getNum(), enable,
scenarioWithBLOBs.getVersionName(), scenarioWithBLOBs.getVersionEnable(), null);
} else {
if (StringUtils.equalsIgnoreCase(element.optString(REFERENCED), REF)) {
element.put(ENABLE, false);
@ -289,7 +306,7 @@ public class MsHashTreeService {
target.forEach(targetObj -> {
JSONObject childTarget = (JSONObject) targetObj;
if (StringUtils.equals(childOrg.optString(ID), childTarget.optString(ID))
&& StringUtils.equals(childOrg.optString(INDEX), childTarget.optString(INDEX))) {
&& StringUtils.equals(childOrg.optString(INDEX), childTarget.optString(INDEX))) {
setRefEnable(childTarget, childOrg);
}
});
@ -303,32 +320,60 @@ public class MsHashTreeService {
return orgElement;
}
public void dataFormatting(JSONArray hashTree) {
public void dataFormatting(JSONArray hashTree, List<String> caseIds) {
for (int i = 0; i < hashTree.length(); i++) {
JSONObject element = hashTree.optJSONObject(i);
if (element != null && StringUtils.equalsIgnoreCase(element.optString(TYPE), SCENARIO)) {
element = this.setRefScenario(element);
hashTree.put(i, element);
} else if (element != null && ElementConstants.REQUESTS.contains(element.optString(TYPE))) {
element = this.setRequest(element);
this.getCaseIds(element, caseIds);
hashTree.put(i, element);
}
if (element.has(HASH_TREE)) {
JSONArray elementJSONArray = element.optJSONArray(HASH_TREE);
dataFormatting(elementJSONArray);
dataFormatting(elementJSONArray, caseIds);
}
}
}
public void dataFormatting(JSONObject element) {
public void dataFormatting(JSONObject element, List<String> caseIds) {
if (element != null && StringUtils.equalsIgnoreCase(element.optString(TYPE), SCENARIO)) {
element = this.setRefScenario(element);
} else if (element != null && ElementConstants.REQUESTS.contains(element.optString(TYPE))) {
element = this.setRequest(element);
this.getCaseIds(element, caseIds);
}
if (element != null && element.has(HASH_TREE)) {
JSONArray elementJSONArray = element.optJSONArray(HASH_TREE);
dataFormatting(elementJSONArray);
dataFormatting(elementJSONArray, caseIds);
}
}
public void caseFormatting(JSONArray hashTree, Map<String, ApiTestCaseInfo> caseMap, ParameterConfig msParameter) {
for (int i = 0; i < hashTree.length(); i++) {
JSONObject element = hashTree.optJSONObject(i);
if (element != null && ElementConstants.REQUESTS.contains(element.optString(TYPE))) {
element = this.setRequest(element, caseMap, msParameter);
hashTree.put(i, element);
}
if (element.has(HASH_TREE)) {
JSONArray elementJSONArray = element.optJSONArray(HASH_TREE);
caseFormatting(elementJSONArray, caseMap, msParameter);
}
}
}
public void caseFormatting(JSONObject element, List<String> caseIds, ParameterConfig msParameter) {
List<ApiTestCaseInfo> caseInfos = apiTestCaseService.selectByCaseIds(caseIds);
Map<String, ApiTestCaseInfo> caseMap = caseInfos.stream()
.collect(Collectors.toMap(ApiTestCase::getId, a -> a, (k1, k2) -> k1));
if (element != null && ElementConstants.REQUESTS.contains(element.optString(TYPE))) {
element = this.setRequest(element, caseMap, msParameter);
}
if (element != null && element.has(HASH_TREE)) {
JSONArray elementJSONArray = element.optJSONArray(HASH_TREE);
caseFormatting(elementJSONArray, caseMap, msParameter);
}
}
}

View File

@ -275,6 +275,12 @@ public class ApiTestCaseService {
return extApiTestCaseMapper.selectApiCaseInfoByPrimaryKey(id);
}
public List<ApiTestCaseInfo> selectByCaseIds(List<String> ids) {
if(CollectionUtils.isEmpty(ids)){
return new ArrayList<>();
}
return extApiTestCaseMapper.selectByCaseIds(ids);
}
public ApiTestCaseInfo getResult(String id) {
return extApiTestCaseMapper.selectApiCaseInfoByPrimaryKey(id);
}

View File

@ -167,7 +167,11 @@ public class ApiScenarioReportStructureService {
//保证场景的步骤是最新的比如场景中包含引用场景
MsHashTreeService hashTreeService = CommonBeanFactory.getBean(MsHashTreeService.class);
assert hashTreeService != null;
hashTreeService.dataFormatting(element);
List<String> caseIds = new ArrayList<>();
hashTreeService.dataFormatting(element, caseIds);
// 处理用例
hashTreeService.caseFormatting(element, caseIds, null);
String resourceId = combinationResourceId(element, reportType, id);
StepTreeDTO dto = new StepTreeDTO(name, resourceId, element.optString(TYPE), resourceId, 1);
dto.setAllIndex(null);

View File

@ -726,22 +726,18 @@ public class ApiScenarioService {
public ApiScenarioDTO getNewApiScenario(String id) {
ApiScenarioDTO scenarioWithBLOBs = extApiScenarioMapper.selectById(id);
if (scenarioWithBLOBs != null && StringUtils.isNotEmpty(scenarioWithBLOBs.getScenarioDefinition())) {
if (scenarioWithBLOBs == null) {
return null;
}
if (StringUtils.isNotEmpty(scenarioWithBLOBs.getScenarioDefinition())) {
JSONObject element = JSONUtil.parseObject(scenarioWithBLOBs.getScenarioDefinition());
hashTreeService.dataFormatting(element);
List<String> caseIds = new ArrayList<>();
hashTreeService.dataFormatting(element, caseIds);
// 处理用例
hashTreeService.caseFormatting(element, caseIds, getConfig(scenarioWithBLOBs));
ElementUtil.dataFormatting(element);
scenarioWithBLOBs.setScenarioDefinition(element.toString());
}
if (scenarioWithBLOBs != null && StringUtils.isNotBlank(scenarioWithBLOBs.getEnvironmentJson())) {
ApiScenarioEnvRequest request = new ApiScenarioEnvRequest();
request.setEnvironmentEnable(false);
request.setDefinition(scenarioWithBLOBs.getScenarioDefinition());
request.setEnvironmentMap(JSON.parseObject(scenarioWithBLOBs.getEnvironmentJson(), Map.class));
request.setEnvironmentType(scenarioWithBLOBs.getEnvironmentType());
request.setEnvironmentGroupId(scenarioWithBLOBs.getEnvironmentGroupId());
request.setId(scenarioWithBLOBs.getId());
scenarioWithBLOBs.setScenarioDefinition(this.setDomain(request));
}
return scenarioWithBLOBs;
}
@ -778,6 +774,26 @@ public class ApiScenarioService {
return target;
}
public ParameterConfig getConfig(ApiScenarioDTO scenario) {
try {
Map<String, String> environmentMap = new HashMap<>();
String environmentType = scenario.getEnvironmentType();
String environmentGroupId = scenario.getEnvironmentGroupId();
String environmentJson = scenario.getEnvironmentJson();
if (StringUtils.equals(environmentType, EnvironmentType.GROUP.name())) {
environmentMap = environmentGroupProjectService.getEnvMap(environmentGroupId);
} else if (StringUtils.equals(environmentType, EnvironmentType.JSON.name())) {
environmentMap = JSON.parseObject(environmentJson, Map.class);
}
ParameterConfig config = new ParameterConfig();
apiScenarioEnvService.setEnvConfig(environmentMap, config);
return config;
} catch (Exception e) {
LogUtil.error(e);
return null;
}
}
public String setDomain(ApiScenarioEnvRequest request) {
Boolean enable = request.getEnvironmentEnable();
String scenarioDefinition = request.getDefinition();
@ -825,14 +841,36 @@ public class ApiScenarioService {
}
}
public List<ApiScenarioDTO> getNewApiScenarios(List<String> ids) {
List<ApiScenarioDTO> list = new LinkedList<>();
if (CollectionUtils.isNotEmpty(ids)) {
ids.forEach(item -> {
ApiScenarioDTO dto = this.getNewApiScenario(item);
list.add(dto);
});
public List<ApiScenarioDTO> getScenarioDetail(List<String> ids) {
if (CollectionUtils.isEmpty(ids)) {
return new ArrayList<>();
}
List<ApiScenarioDTO> list = extApiScenarioMapper.selectByScenarioIds(ids);
list.forEach(dto -> {
if (dto != null) {
if (StringUtils.isNotEmpty(dto.getScenarioDefinition())) {
JSONObject element = JSONUtil.parseObject(dto.getScenarioDefinition());
// 获取所有case
List<String> caseIds = new ArrayList<>();
hashTreeService.dataFormatting(element, caseIds);
// 处理用例
hashTreeService.caseFormatting(element, caseIds, null);
ElementUtil.dataFormatting(element);
dto.setScenarioDefinition(element.toString());
}
if (StringUtils.isNotBlank(dto.getEnvironmentJson())) {
ApiScenarioEnvRequest request = new ApiScenarioEnvRequest();
request.setEnvironmentEnable(false);
request.setDefinition(dto.getScenarioDefinition());
request.setEnvironmentMap(JSON.parseObject(dto.getEnvironmentJson(), Map.class));
request.setEnvironmentType(dto.getEnvironmentType());
request.setEnvironmentGroupId(dto.getEnvironmentGroupId());
request.setId(dto.getId());
dto.setScenarioDefinition(this.setDomain(request));
}
}
});
return list;
}