fix(测试计划执行): 修复测试计划执行报告显示不准确的缺陷

修复测试计划报告中接口案例只显示最新执行结果的问题,以及场景用例未执行时显示最近一次执行报告的问题。
This commit is contained in:
song-tianyang 2021-11-19 00:45:08 +08:00 committed by song-tianyang
parent f0a0410df4
commit 1badc4e1f6
14 changed files with 161 additions and 95 deletions

View File

@ -10,4 +10,5 @@ import lombok.Setter;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class TestPlanFailureApiDTO extends TestPlanApiCaseDTO {
private String response;
private String reportId;
}

View File

@ -1230,8 +1230,6 @@ public class ApiAutomationService {
String scenarioId = entry.getValue();
ApiScenarioWithBLOBs scenario = scenarioMap.get(scenarioId);
// }
// for (ApiScenarioWithBLOBs scenario : apiScenarios) {
if (scenario.getStepTotal() == null || scenario.getStepTotal() == 0) {
continue;
}
@ -1239,16 +1237,12 @@ public class ApiAutomationService {
Map<String, String> planEnvMap = new HashMap<>();
//测试计划页面触发的执行方式生成报告时createScenarioReport第二个参数需要特殊处理
// String testPlanScenarioId = scenario.getId();
// if (request.getScenarioTestPlanIdMap() != null && request.getScenarioTestPlanIdMap().containsKey(item.getId())) {
// testPlanScenarioId = request.getScenarioTestPlanIdMap().get(item.getId());
// 获取场景用例单独的执行环境
TestPlanApiScenario planApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(testPlanScenarioId);
String environment = planApiScenario.getEnvironment();
if (StringUtils.isNotBlank(environment)) {
planEnvMap = JSON.parseObject(environment, Map.class);
}
// }
if(StringUtils.isEmpty(projectId)){
projectId = testPlanScenarioCaseService.getProjectIdById(testPlanScenarioId);
}

View File

@ -423,4 +423,19 @@ public class ApiDefinitionExecResultService {
return returnList;
}
}
public Map<String, String> selectReportResultByReportIds(Collection<String> values) {
if(CollectionUtils.isEmpty(values)){
return new HashMap<>();
}else {
Map<String, String> returnMap = new HashMap<>();
List<ApiDefinitionExecResult> idStatusList = extApiDefinitionExecResultMapper.selectStatusByIdList(values);
for (ApiDefinitionExecResult model: idStatusList){
String id = model.getId();
String status = model.getStatus();
returnMap.put(id,status);
}
return returnMap;
}
}
}

View File

@ -863,4 +863,15 @@ public class ApiScenarioReportService {
return count;
}
public Map<String, String> getReportStatusByReportIds(Collection<String> values) {
if(CollectionUtils.isEmpty(values)){
return new HashMap<>();
}
Map<String, String> map = new HashMap<>();
List<ApiScenarioReport> reportList = extApiScenarioReportMapper.selectStatusByIds(values);
for (ApiScenarioReport report : reportList) {
map.put(report.getId(),report.getStatus());
}
return map;
}
}

View File

@ -4,6 +4,7 @@ import io.metersphere.api.dto.datacount.ExecutedCaseInfoResult;
import io.metersphere.base.domain.ApiDefinitionExecResult;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
import java.util.List;
public interface ExtApiDefinitionExecResultMapper {
@ -24,4 +25,6 @@ public interface ExtApiDefinitionExecResultMapper {
String selectExecResult(String resourceId);
ApiDefinitionExecResult selectPlanApiMaxResultByTestIdAndType(String resourceId, String type);
List<ApiDefinitionExecResult> selectStatusByIdList(@Param("ids")Collection<String> values);
}

View File

@ -92,4 +92,14 @@
where resource_id = #{resourceId,jdbcType=VARCHAR} and `type` = #{type, jdbcType=VARCHAR}
ORDER BY start_time DESC LIMIT 1
</select>
<select id="selectStatusByIdList"
resultType="io.metersphere.base.domain.ApiDefinitionExecResult">
select id,status from api_definition_exec_result
where id in
<foreach collection="ids" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</select>
</mapper>

View File

@ -7,6 +7,7 @@ import io.metersphere.base.domain.ApiScenarioReport;
import io.metersphere.dto.ApiReportCountDTO;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
import java.util.List;
public interface ExtApiScenarioReportMapper {
@ -29,4 +30,6 @@ public interface ExtApiScenarioReportMapper {
List<String> idList(@Param("request") QueryAPIReportRequest request);
List<ApiReportCountDTO> countByApiScenarioId();
List<ApiScenarioReport> selectStatusByIds(@Param("ids") Collection<String> values);
}

View File

@ -285,4 +285,11 @@
WHERE scenario_id is not null GROUP BY scenario_id;
</select>
<select id="selectStatusByIds" resultType="io.metersphere.base.domain.ApiScenarioReport">
select id,status from api_scenario_report
WHERE id IN
<foreach collection="ids" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</select>
</mapper>

View File

@ -24,6 +24,7 @@ public class TestPlanLoadCaseDTO extends TestPlanLoadCaseWithBLOBs {
private String num;
private String name;
private ResponseDTO response;
private String reportId;
@Getter
@Setter

View File

@ -419,10 +419,23 @@ public class TestPlanApiCaseService {
// 开始选择执行模式
if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) {
Map<TestPlanApiCase, ApiDefinitionExecResult> executeQueue = new HashMap<>();
//记录案例线程结果以及执行失败的案例ID
Map<String, String> executeThreadIdMap = new HashMap<>();
planApiCases.forEach(testPlanApiCase -> {
ApiDefinitionExecResult report = addResult(request, testPlanApiCase, APITestStatus.Waiting.name(), batchMapper);
executeQueue.put(testPlanApiCase, report);
executeThreadIdMap.put(testPlanApiCase.getId(),report.getId());
});
//如果是测试计划生成报告的执行则更新执行信息执行线程信息
if(TestPlanReportExecuteCatch.containsReport(request.getPlanReportId())){
if (!executeThreadIdMap.isEmpty()) {
TestPlanReportExecuteCatch.updateTestPlanThreadInfo(request.getPlanReportId(), executeThreadIdMap,null, null);
}
}
sqlSession.commit();
List<String> reportIds = new LinkedList<>();
// 开始串行执行
@ -433,8 +446,6 @@ public class TestPlanApiCaseService {
try {
Thread.currentThread().setName("TestPlanCase串行执行线程");
//记录案例线程结果以及执行失败的案例ID
Map<String, String> executeThreadIdMap = new HashMap<>();
List<String> executeErrorList = new ArrayList<>();
for (TestPlanApiCase testPlanApiCase : executeQueue.keySet()) {
@ -479,7 +490,7 @@ public class TestPlanApiCaseService {
break;
}
}
executeThreadIdMap.put(testPlanApiCase.getId(),randomUUID);
} catch (Exception e) {
executeErrorList.add(testPlanApiCase.getId());
reportIds.remove(executeQueue.get(testPlanApiCase).getId());
@ -507,9 +518,6 @@ public class TestPlanApiCaseService {
}
TestPlanReportExecuteCatch.updateApiTestPlanExecuteInfo(request.getPlanReportId(), executeErrorMap, null, null);
}
if (!executeThreadIdMap.isEmpty()) {
TestPlanReportExecuteCatch.updateTestPlanThreadInfo(request.getPlanReportId(), executeThreadIdMap,null, null);
}
}
} catch (Exception e) {
@ -522,17 +530,23 @@ public class TestPlanApiCaseService {
thread.start();
} else {
Map<String, TestPlanApiCase> executeQueue = new HashMap<>();
//记录案例线程结果以及执行失败的案例ID
Map<String, String> executeThreadIdMap = new HashMap<>();
planApiCases.forEach(testPlanApiCase -> {
ApiDefinitionExecResult report = addResult(request, testPlanApiCase, APITestStatus.Running.name(), batchMapper);
executeQueue.put(report.getId(), testPlanApiCase);
executeThreadIdMap.put(testPlanApiCase.getId(),report.getId());
MessageCache.caseExecResourceLock.put(report.getId(), report);
});
sqlSession.commit();
//如果是测试计划生成报告的执行则更新执行信息执行线程信息
if(TestPlanReportExecuteCatch.containsReport(request.getPlanReportId())){
if (!executeThreadIdMap.isEmpty()) {
TestPlanReportExecuteCatch.updateTestPlanThreadInfo(request.getPlanReportId(), executeThreadIdMap,null, null);
}
}
// 开始并发执行
//记录案例线程结果以及执行失败的案例ID
Map<String, String> executeThreadIdMap = new HashMap<>();
List<String> executeErrorList = new ArrayList<>();
for (String reportId : executeQueue.keySet()) {
@ -542,7 +556,6 @@ public class TestPlanApiCaseService {
if (request.getConfig() != null && StringUtils.isNotEmpty(request.getConfig().getResourcePoolId())) {
String testId = testPlanApiCase.getId()+":"+ request.getPlanReportId() + ":" +reportId;
jMeterService.runTest(testId, reportId, request.getTriggerMode(), request.getPlanReportId(), request.getConfig());
executeThreadIdMap.put(testPlanApiCase.getApiCaseId(),testPlanApiCase.getId());
} else {
HashTree hashTree = generateHashTree(testPlanApiCase.getId());
if(StringUtils.isEmpty(debugId)){
@ -550,7 +563,6 @@ public class TestPlanApiCaseService {
}
String testId = reportId+":"+ request.getPlanReportId();
jMeterService.runLocal(testId,request.getConfig(), hashTree, debugId, request.getTriggerMode());
executeThreadIdMap.put(testPlanApiCase.getId(),reportId);
}
}catch (Exception e){
executeErrorList.add(testPlanApiCase.getId());
@ -567,9 +579,6 @@ public class TestPlanApiCaseService {
}
TestPlanReportExecuteCatch.updateApiTestPlanExecuteInfo(request.getPlanReportId(), executeErrorMap, null, null);
}
if (!executeThreadIdMap.isEmpty()) {
TestPlanReportExecuteCatch.updateTestPlanThreadInfo(request.getPlanReportId(), executeThreadIdMap,null, null);
}
}
}
return request.getId();
@ -678,14 +687,6 @@ public class TestPlanApiCaseService {
return buildCases(apiTestCases);
}
public List<TestPlanFailureApiDTO> getAllCases(Collection<String> caseIdList,String status) {
if (caseIdList.isEmpty()) {
return new ArrayList<>();
}
List<TestPlanFailureApiDTO> apiTestCases = extTestPlanApiCaseMapper.getFailureListByIds(caseIdList, status);
return buildCases(apiTestCases);
}
public List<TestPlanFailureApiDTO> buildCases(List<TestPlanFailureApiDTO> apiTestCases) {
if (CollectionUtils.isEmpty(apiTestCases)) {
return apiTestCases;
@ -713,4 +714,30 @@ public class TestPlanApiCaseService {
testPlanApiCaseMapper::updateByPrimaryKeySelective);
}
public List<TestPlanFailureApiDTO> getByApiExecReportIds(Map<String,String> testPlanApiCaseReportMap,boolean isFinish) {
if (testPlanApiCaseReportMap.isEmpty()) {
return new ArrayList<>();
}
String defaultStatus = "Running";
if(isFinish){
defaultStatus = "error";
}
List<TestPlanFailureApiDTO> apiTestCases = extTestPlanApiCaseMapper.getFailureListByIds(testPlanApiCaseReportMap.keySet(),null);
Map<String,String> reportResult = apiDefinitionExecResultService.selectReportResultByReportIds(testPlanApiCaseReportMap.values());
for (TestPlanFailureApiDTO dto : apiTestCases) {
String testPlanApiCaseId = dto.getId();
String reportId = testPlanApiCaseReportMap.get(testPlanApiCaseId);
dto.setReportId(reportId);
if(StringUtils.isEmpty(reportId)){
dto.setStatus(defaultStatus);
}else {
String status = reportResult.get(reportId);
if(status == null){
status = defaultStatus;
}
dto.setStatus(status);
}
}
return buildCases(apiTestCases);
}
}

View File

@ -595,7 +595,7 @@ public class TestPlanReportService {
Map<String, Map<String, String>> testPlanExecuteResult = executeInfo.getExecutedResult();
testPlanLog.info("ReportId[" + testPlanReport.getId() + "] COUNT OVER. COUNT RESULT :" + JSONObject.toJSONString(testPlanExecuteResult));
TestPlanSimpleReportDTO reportDTO = testPlanService.buildPlanReport(executeInfo, testPlanReport.getTestPlanId(), false);
TestPlanSimpleReportDTO reportDTO = testPlanService.buildPlanReport(executeInfo, testPlanReport.getTestPlanId(), apiCaseIsOk && scenarioIsOk && performanceIsOk);
reportDTO.setStartTime(testPlanReport.getStartTime());
long endTime = System.currentTimeMillis();
//全部结束时更新时间
@ -1102,7 +1102,7 @@ public class TestPlanReportService {
//如果间隔超过5分钟没有案例执行完成则把执行结果变成false
long lastCountTime = executeInfo.getLastFinishedNumCountTime();
long nowTime = System.currentTimeMillis();
if (nowTime - lastCountTime > 300000) {
if (nowTime - lastCountTime > 1800000) {
TestPlanReportExecuteCatch.finishAllTask(planReportId);
}
}

View File

@ -450,9 +450,27 @@ public class TestPlanScenarioCaseService {
return buildCases(apiTestCases);
}
public List<TestPlanFailureScenarioDTO> getAllCases(Collection<String> ids, String status) {
public List<TestPlanFailureScenarioDTO> getAllCases(Map<String,String> idMap, boolean isFinish) {
List<TestPlanFailureScenarioDTO> apiTestCases =
extTestPlanScenarioCaseMapper.getFailureListByIds(ids, status);
extTestPlanScenarioCaseMapper.getFailureListByIds(idMap.keySet(), null);
String defaultStatus = "Running";
if(isFinish){
defaultStatus = "Error";
}
Map<String,String> reportStatus = apiScenarioReportService.getReportStatusByReportIds(idMap.values());
for (TestPlanFailureScenarioDTO dto: apiTestCases) {
String reportId = idMap.get(dto.getId());
dto.setReportId(reportId);
if(reportId != null){
String status = reportStatus.get(reportId);
if(status == null ){
status = defaultStatus;
}
dto.setStatus(status);
}
}
return buildCases(apiTestCases);
}

View File

@ -36,7 +36,6 @@ import io.metersphere.log.utils.ReflexObjectUtil;
import io.metersphere.log.vo.DetailColumn;
import io.metersphere.log.vo.OperatingLogDetails;
import io.metersphere.log.vo.track.TestPlanReference;
import io.metersphere.notice.service.NoticeSendService;
import io.metersphere.performance.base.*;
import io.metersphere.performance.dto.LoadTestExportJmx;
import io.metersphere.performance.dto.MetricData;
@ -1118,7 +1117,7 @@ public class TestPlanService {
//执行性能测试任务
Map<String, String> performaneReportIDMap = new LinkedHashMap<>();
Map<String, String> performaneThreadIDMap = new LinkedHashMap<>();
for (Map.Entry<String, String> entry : performanceIdMap.entrySet()) {
String id = entry.getKey();
String caseID = entry.getValue();
@ -1155,12 +1154,14 @@ public class TestPlanService {
} catch (Exception e) {
e.printStackTrace();
}
performaneThreadIDMap.put(performanceRequest.getTestPlanLoadId(),reportId);
if (StringUtils.isNotEmpty(reportId)) {
executePerformanceIdMap.put(caseID, TestPlanApiExecuteStatus.RUNNING.name());
} else {
executePerformanceIdMap.put(caseID, TestPlanApiExecuteStatus.PREPARE.name());
}
}
TestPlanReportExecuteCatch.updateTestPlanThreadInfo(planReportId,null,null,performaneThreadIDMap);
if (!performaneReportIDMap.isEmpty()) {
//性能测试时保存性能测试报告ID在结果返回时用于捕捉并进行
testPlanReportService.updatePerformanceInfo(testPlanReport, performaneReportIDMap, triggerMode);
@ -1707,7 +1708,7 @@ public class TestPlanService {
}
}
public void buildApiReport(TestPlanSimpleReportDTO report, JSONObject config, TestPlanExecuteInfo executeInfo, String planId, boolean saveResponse) {
public void buildApiReport(TestPlanSimpleReportDTO report, JSONObject config, TestPlanExecuteInfo executeInfo,boolean isFinish) {
if (MapUtils.isEmpty(executeInfo.getApiCaseExecInfo()) && MapUtils.isEmpty(executeInfo.getApiScenarioCaseExecInfo())) {
return;
}
@ -1718,33 +1719,12 @@ public class TestPlanService {
if (checkReportConfig(config, "api", "all")) {
if (MapUtils.isNotEmpty(executeInfo.getApiCaseExecInfo())) {
// 接口
apiAllCases = testPlanApiCaseService.getAllCases(executeInfo.getApiCaseExecInfo().keySet(), null);
if (saveResponse) {
apiAllCases.forEach(item -> {
String apiReportid = executeInfo.getApiCaseReportMap().get(item.getId());
ApiDefinitionExecResult result = apiDefinitionExecResultMapper.selectByPrimaryKey(apiReportid);
if (result != null) {
APIReportResult dbResult = apiDefinitionService.buildAPIReportResult(result);
if (dbResult != null && StringUtils.isNotBlank(dbResult.getContent())) {
item.setResponse(dbResult.getContent());
}
}
});
}
apiAllCases = testPlanApiCaseService.getByApiExecReportIds(executeInfo.getApiCaseExecuteThreadMap(),isFinish);
report.setApiAllCases(apiAllCases);
}
if (MapUtils.isNotEmpty(executeInfo.getApiScenarioCaseExecInfo())) {
//场景
scenarioAllCases = testPlanScenarioCaseService.getAllCases(executeInfo.getApiScenarioCaseExecInfo().keySet(), null);
if (saveResponse) {
scenarioAllCases.forEach((item) -> {
String resultId = executeInfo.getApiScenarioReportMap().get(item.getId());
APIScenarioReportResult result = apiScenarioReportService.get(resultId);
if (result != null) {
item.setResponse(result);
}
});
}
scenarioAllCases = testPlanScenarioCaseService.getAllCases(executeInfo.getApiScenarioThreadMap(),isFinish);
report.setScenarioAllCases(scenarioAllCases);
}
}
@ -1756,18 +1736,6 @@ public class TestPlanService {
.filter(i -> StringUtils.isNotBlank(i.getExecResult())
&& i.getExecResult().equals("error"))
.collect(Collectors.toList());
if (saveResponse) {
for (TestPlanFailureApiDTO item : apiFailureCases) {
String apiReportid = executeInfo.getApiCaseReportMap().get(item.getId());
ApiDefinitionExecResult result = apiDefinitionExecResultMapper.selectByPrimaryKey(apiReportid);
if (result != null) {
APIReportResult dbResult = apiDefinitionService.buildAPIReportResult(result);
if (dbResult != null && StringUtils.isNotBlank(dbResult.getContent())) {
item.setResponse(dbResult.getContent());
}
}
}
}
}
report.setApiFailureCases(apiFailureCases);
@ -1778,15 +1746,6 @@ public class TestPlanService {
.filter(i -> StringUtils.isNotBlank(i.getLastResult())
&& i.getLastResult().equals("Fail"))
.collect(Collectors.toList());
if (saveResponse) {
for (TestPlanFailureScenarioDTO item : scenarioFailureCases) {
String resultId = executeInfo.getApiScenarioReportMap().get(item.getId());
APIScenarioReportResult result = apiScenarioReportService.get(resultId);
if (result != null) {
item.setResponse(result);
}
}
}
}
report.setScenarioFailureCases(scenarioFailureCases);
}
@ -1801,6 +1760,11 @@ public class TestPlanService {
List<TestPlanLoadCaseDTO> allCases = null;
if (checkReportConfig(config, "load", "all")) {
allCases = testPlanLoadCaseService.getAllCases(executeInfo.getLoadCaseExecInfo().keySet(), planId, null);
for (TestPlanLoadCaseDTO dto :
allCases) {
String reportId = executeInfo.getLoadCaseReportIdMap().get(dto.getId());
dto.setReportId(reportId);
}
if (saveResponse) {
buildLoadResponse(allCases);
}
@ -1819,7 +1783,7 @@ public class TestPlanService {
}
}
public TestPlanSimpleReportDTO buildPlanReport(TestPlanExecuteInfo executeInfo, String planId, boolean saveResponse) {
public TestPlanSimpleReportDTO buildPlanReport(TestPlanExecuteInfo executeInfo, String planId, boolean isFinish) {
TestPlanWithBLOBs testPlan = testPlanMapper.selectByPrimaryKey(planId);
if (testPlan != null) {
String reportConfig = testPlan.getReportConfig();
@ -1829,8 +1793,8 @@ public class TestPlanService {
}
TestPlanSimpleReportDTO report = getReport(planId);
buildFunctionalReport(report, config, planId);
buildApiReport(report, config, executeInfo, planId, true);
buildLoadReport(report, config, executeInfo, planId, saveResponse);
buildApiReport(report, config, executeInfo, isFinish);
buildLoadReport(report, config, executeInfo, planId, false);
return report;
} else {
return null;

View File

@ -157,7 +157,18 @@ export default {
}
});
} else {
// todo
if(row.reportId){
let url = "/api/definition/report/get/" + row.reportId;
this.$get(url, response => {
if (response.data) {
let data = response.data;
if (data && data.content) {
this.showResponse = true;
this.response = JSON.parse(data.content);
}
}
});
}else {
getApiReport(row.id, (data) => {
if (data && data.content) {
this.showResponse = true;
@ -168,6 +179,7 @@ export default {
}
}
}
}
</script>
<style scoped>