refactor(测试跟踪):重构接口用例批量执行方法,和场景执行保持一致。

This commit is contained in:
fit2-zhao 2021-06-15 16:19:52 +08:00 committed by fit2-zhao
parent 5eaae502d4
commit b0232c21c0
11 changed files with 111 additions and 96 deletions

View File

@ -8,6 +8,8 @@ import lombok.Setter;
@Setter
public class RunRequest {
private String testId;
// api / case 或有这个属性值
private String reportId;
private String url;
private String userId;
private boolean isDebug;

View File

@ -152,7 +152,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
TestResult testResult = new TestResult();
testResult.setTestId(testId);
testResult.setTotal(queue.size());
testResult.setConsole(getConsole());
testResult.setConsole(getConsole());
// 一个脚本里可能包含多个场景(ThreadGroup)所以要区分开key: 场景Id
final Map<String, ScenarioResult> scenarios = new LinkedHashMap<>();

View File

@ -327,6 +327,9 @@ public class JMeterService {
try {
RunRequest runRequest = new RunRequest();
runRequest.setTestId(testId);
if (ApiRunMode.API_PLAN.name().equals(runMode)) {
runRequest.setReportId(reportId);
}
metersphereUrl += "/api/jmeter/download?testId=" + testId + "&reportId=" + reportId + "&testPlanScenarioId" + "&runMode=" + runMode;
if (StringUtils.isNotEmpty(testPlanScenarioId)) {
metersphereUrl += "=" + testPlanScenarioId;

View File

@ -52,6 +52,8 @@ public class MsKafkaListener {
private TestPlanReportService testPlanReportService;
@Resource
private ApiScenarioReportService apiScenarioReportService;
@Resource
private ApiEnvironmentRunningParamService apiEnvironmentRunningParamService;
private TestResult formatResult(String result) {
ObjectMapper mapper = new ObjectMapper();
@ -61,6 +63,10 @@ public class MsKafkaListener {
if (StringUtils.isNotEmpty(result)) {
TestResult element = mapper.readValue(result, new TypeReference<TestResult>() {
});
if (StringUtils.isNotEmpty(element.getRunningDebugSampler())) {
String evnStr = element.getRunningDebugSampler();
apiEnvironmentRunningParamService.parseEvn(evnStr);
}
return element;
}
} catch (Exception e) {
@ -79,15 +85,15 @@ public class MsKafkaListener {
// 调试操作不需要存储结果
apiDefinitionService.addResult(testResult);
if (!testResult.isDebug()) {
apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.DEFINITION.name(),TriggerMode.DEBUG.name());
apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.DEFINITION.name(), TriggerMode.DEBUG.name());
}
} else if (StringUtils.equals(testResult.getRunMode(), ApiRunMode.JENKINS.name())) {
apiDefinitionService.addResult(testResult);
apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.DEFINITION.name(),TriggerMode.DEBUG.name());
apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.DEFINITION.name(), TriggerMode.DEBUG.name());
} else if (StringUtils.equals(testResult.getRunMode(), ApiRunMode.JENKINS_API_PLAN.name())) {
apiDefinitionService.addResult(testResult);
apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.API_PLAN.name(),TriggerMode.DEBUG.name());
apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.API_PLAN.name(), TriggerMode.DEBUG.name());
ApiDefinitionExecResult result = apiDefinitionService.getResultByJenkins(testResult.getTestId(), ApiRunMode.API_PLAN.name());
if (result != null) {
report = new ApiTestReport();
@ -109,7 +115,7 @@ public class MsKafkaListener {
}
testPlanReportService.updateReport(testPlanReportIdList, ApiRunMode.SCHEDULE_API_PLAN.name(), ReportTriggerMode.SCHEDULE.name());
} else {
apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.API_PLAN.name(),TriggerMode.DEBUG.name());
apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.API_PLAN.name(), TriggerMode.DEBUG.name());
}
} else if (StringUtils.equalsAny(testResult.getRunMode(), ApiRunMode.SCENARIO.name(), ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO.name())) {
// 执行报告不需要存储由用户确认后在存储

View File

@ -40,6 +40,8 @@ public class TestResult {
private String console;
private String runningDebugSampler;
private List<ScenarioResult> scenarios = new ArrayList<>();
private Map<String, Boolean> margeScenariMap = new HashMap<>();

View File

@ -61,36 +61,41 @@ public class ApiDefinitionExecResultService {
result.getScenarios().forEach(scenarioResult -> {
if (scenarioResult != null && CollectionUtils.isNotEmpty(scenarioResult.getRequestResults())) {
scenarioResult.getRequestResults().forEach(item -> {
ApiDefinitionExecResult saveResult = definitionExecResultMapper.selectByPrimaryKey(result.getTestId());
item.getResponseResult().setConsole(result.getConsole());
ApiDefinitionExecResult saveResult = new ApiDefinitionExecResult();
saveResult.setId(UUID.randomUUID().toString());
saveResult.setCreateTime(item.getStartTime());
if (StringUtils.isNotEmpty(result.getUserId())) {
saveResult.setUserId(result.getUserId());
} else {
saveResult.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId());
boolean saved = true;
if (saveResult == null) {
saveResult = new ApiDefinitionExecResult();
saveResult.setId(UUID.randomUUID().toString());
saveResult.setActuator("LOCAL");
saveResult.setName(item.getName());
saveResult.setTriggerMode(triggerMode);
saveResult.setType(type);
ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = apiDefinitionMapper.selectByPrimaryKey(item.getName());
if (apiDefinitionWithBLOBs != null) {
saveResult.setName(apiDefinitionWithBLOBs.getName());
} else {
ApiTestCaseWithBLOBs caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(item.getName());
if (caseWithBLOBs != null) {
saveResult.setName(caseWithBLOBs.getName());
}
}
if (StringUtils.isNotEmpty(result.getUserId())) {
saveResult.setUserId(result.getUserId());
} else {
saveResult.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId());
}
saved = false;
}
saveResult.setName(item.getName());
saveResult.setTriggerMode(triggerMode);
saveResult.setActuator("LOCAL");
saveResult.setCreateTime(item.getStartTime());
if (item.getName().indexOf(DelimiterConstants.SEPARATOR.toString()) != -1) {
saveResult.setName(item.getName().substring(0, item.getName().indexOf(DelimiterConstants.SEPARATOR.toString())));
}
ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = apiDefinitionMapper.selectByPrimaryKey(item.getName());
if (apiDefinitionWithBLOBs != null) {
saveResult.setName(apiDefinitionWithBLOBs.getName());
} else {
ApiTestCaseWithBLOBs caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(item.getName());
if (caseWithBLOBs != null) {
saveResult.setName(caseWithBLOBs.getName());
}
}
saveResult.setResourceId(item.getName());
saveResult.setContent(JSON.toJSONString(item));
saveResult.setStartTime(item.getStartTime());
String status = item.isSuccess() ? "success" : "error";
saveResult.setEndTime(item.getResponseResult().getResponseTime());
saveResult.setType(type);
saveResult.setStatus(status);
if (StringUtils.equals(type, ApiRunMode.API_PLAN.name())) {
testPlanApiCaseService.setExecResult(item.getName(), status, item.getStartTime());
@ -109,7 +114,11 @@ public class ApiDefinitionExecResultService {
apiTestCaseWithBLOBs.setLastResultId(saveResult.getId());
apiTestCaseMapper.updateByPrimaryKeySelective(apiTestCaseWithBLOBs);
definitionExecResultMapper.insert(saveResult);
if (!saved) {
definitionExecResultMapper.insert(saveResult);
} else {
definitionExecResultMapper.updateByPrimaryKeyWithBLOBs(saveResult);
}
});
}
});

View File

@ -15,8 +15,9 @@
</if>
)
UNION ALL
(select t.id,t.name,'API' as executionModule, ifnull(t.actuator,'LOCAL') as actuator, t1.`name` as executor,t.create_time as executionTime, ifnull(t.trigger_mode,'MANUAL') as triggerMode ,ifnull(t.status,'Saved') as executionStatus
from api_definition_exec_result t left join `user` t1 ON t.user_id = t1.id left join api_definition t3 on t.resource_id = t3.id left join api_test_case t4 on t4.id = t.resource_id
(select t.id,t.name,'API' as executionModule, ifnull(t2.name,'LOCAL') as actuator, t1.`name` as executor,t.create_time as executionTime, ifnull(t.trigger_mode,'MANUAL') as triggerMode ,ifnull(t.status,'Saved') as executionStatus
from api_definition_exec_result t left join `user` t1 ON t.user_id = t1.id left join test_resource_pool t2 on t.actuator = t2.id
left join api_definition t3 on t.resource_id = t3.id left join api_test_case t4 on t4.id = t.resource_id
where to_days(FROM_UNIXTIME(t.create_time/1000))= to_days(now()) and (t3.project_id =#{request.projectId} OR t4.project_id =#{request.projectId})
<if test="request.triggerMode != null and request.triggerMode != ''">
and t.trigger_mode = #{request.triggerMode}

View File

@ -65,7 +65,7 @@ public class TestPlanApiCaseController {
}
@PostMapping(value = "/run")
public String run(@RequestPart("request") BatchRunDefinitionRequest request) {
public String run(@RequestBody BatchRunDefinitionRequest request) {
return testPlanApiCaseService.run(request);
}
}

View File

@ -29,8 +29,10 @@ import io.metersphere.base.mapper.ApiTestCaseMapper;
import io.metersphere.base.mapper.TestPlanApiCaseMapper;
import io.metersphere.base.mapper.TestPlanMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanApiCaseMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.RunModeConstants;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*;
import io.metersphere.dto.BaseSystemConfigDTO;
@ -252,10 +254,11 @@ public class TestPlanApiCaseService {
}
private MsTestElement parse(String api, String planId) {
private MsTestElement parse(ApiTestCaseWithBLOBs caseWithBLOBs, String planId) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
try {
String api = caseWithBLOBs.getRequest();
JSONObject element = JSON.parseObject(api);
LinkedList<MsTestElement> list = new LinkedList<>();
if (element != null && StringUtils.isNotEmpty(element.getString("hashTree"))) {
@ -265,40 +268,32 @@ public class TestPlanApiCaseService {
list.addAll(elements);
}
TestPlanApiCase apiCase = testPlanApiCaseMapper.selectByPrimaryKey(planId);
Map<String, String> envMap = null;
if (apiCase != null) {
envMap = JSON.parseObject(apiCase.getEnvironmentId(), Map.class);
}
if (element.getString("type").equals("HTTPSamplerProxy")) {
MsHTTPSamplerProxy httpSamplerProxy = JSON.parseObject(api, MsHTTPSamplerProxy.class);
httpSamplerProxy.setHashTree(list);
if (envMap != null && envMap.containsKey(httpSamplerProxy.getProjectId())) {
httpSamplerProxy.setUseEnvironment(envMap.get(httpSamplerProxy.getProjectId()));
}
httpSamplerProxy.setName(caseWithBLOBs.getId());
httpSamplerProxy.setUseEnvironment(apiCase.getEnvironmentId());
return httpSamplerProxy;
}
if (element.getString("type").equals("TCPSampler")) {
MsTCPSampler msTCPSampler = JSON.parseObject(api, MsTCPSampler.class);
if (envMap != null && envMap.containsKey(msTCPSampler.getProjectId())) {
msTCPSampler.setUseEnvironment(envMap.get(msTCPSampler.getProjectId()));
}
msTCPSampler.setUseEnvironment(apiCase.getEnvironmentId());
msTCPSampler.setHashTree(list);
msTCPSampler.setName(caseWithBLOBs.getId());
return msTCPSampler;
}
if (element.getString("type").equals("DubboSampler")) {
MsDubboSampler dubboSampler = JSON.parseObject(api, MsDubboSampler.class);
if (envMap != null && envMap.containsKey(dubboSampler.getProjectId())) {
dubboSampler.setUseEnvironment(envMap.get(dubboSampler.getProjectId()));
}
dubboSampler.setUseEnvironment(apiCase.getEnvironmentId());
dubboSampler.setHashTree(list);
dubboSampler.setName(caseWithBLOBs.getId());
return dubboSampler;
}
if (element.getString("type").equals("JDBCSampler")) {
MsJDBCSampler jDBCSampler = JSON.parseObject(api, MsJDBCSampler.class);
if (envMap != null && envMap.containsKey(jDBCSampler.getProjectId())) {
jDBCSampler.setUseEnvironment(envMap.get(jDBCSampler.getProjectId()));
}
jDBCSampler.setUseEnvironment(apiCase.getEnvironmentId());
jDBCSampler.setHashTree(list);
jDBCSampler.setName(caseWithBLOBs.getId());
return jDBCSampler;
}
} catch (Exception e) {
@ -319,8 +314,8 @@ public class TestPlanApiCaseService {
try {
MsThreadGroup group = new MsThreadGroup();
group.setLabel(caseWithBLOBs.getName());
group.setName(testId);
MsTestElement testElement = parse(caseWithBLOBs.getRequest(), testId);
group.setName(caseWithBLOBs.getName());
MsTestElement testElement = parse(caseWithBLOBs, testId);
group.setHashTree(new LinkedList<>());
group.getHashTree().add(testElement);
testPlan.getHashTree().add(group);
@ -334,12 +329,36 @@ public class TestPlanApiCaseService {
return null;
}
private String addResult(BatchRunDefinitionRequest request, TestPlanApiCase key) {
ApiDefinitionExecResult apiResult = new ApiDefinitionExecResult();
apiResult.setId(UUID.randomUUID().toString());
apiResult.setCreateTime(System.currentTimeMillis());
apiResult.setStartTime(System.currentTimeMillis());
apiResult.setEndTime(System.currentTimeMillis());
ApiTestCaseWithBLOBs caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(key.getApiCaseId());
if (caseWithBLOBs != null) {
apiResult.setName(caseWithBLOBs.getName());
}
apiResult.setTriggerMode(TriggerMode.MANUAL.name());
apiResult.setActuator("LOCAL");
if (request.getConfig() != null && StringUtils.isNotEmpty(request.getConfig().getResourcePoolId())) {
apiResult.setActuator(request.getConfig().getResourcePoolId());
}
apiResult.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId());
apiResult.setResourceId(key.getApiCaseId());
apiResult.setStartTime(System.currentTimeMillis());
apiResult.setType(ApiRunMode.API_PLAN.name());
apiResult.setStatus(APITestStatus.Running.name());
mapper.insert(apiResult);
return apiResult.getId();
}
public String modeRun(BatchRunDefinitionRequest request) {
List<String> ids = request.getPlanIds();
TestPlanApiCaseExample example = new TestPlanApiCaseExample();
example.createCriteria().andIdIn(ids);
List<TestPlanApiCase> planApiCases = testPlanApiCaseMapper.selectByExample(example);
// 开始选择执行模式
ExecutorService executorService = Executors.newFixedThreadPool(planApiCases.size());
if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) {
@ -357,6 +376,8 @@ public class TestPlanApiCaseService {
HashTree hashTree = generateHashTree(key.getId());
modeDataDTO = new RunModeDataDTO(hashTree, UUID.randomUUID().toString());
}
String reportId = addResult(request, key);
modeDataDTO.setReportId(reportId);
Future<ApiDefinitionExecResult> future = executorService.submit(new SerialApiExecTask(jMeterService, mapper, modeDataDTO, request.getConfig(), ApiRunMode.API_PLAN.name()));
ApiDefinitionExecResult report = future.get();
// 如果开启失败结束执行则判断返回结果状态
@ -384,6 +405,8 @@ public class TestPlanApiCaseService {
HashTree hashTree = generateHashTree(key.getId());
modeDataDTO = new RunModeDataDTO(hashTree, UUID.randomUUID().toString());
}
String reportId = addResult(request, key);
modeDataDTO.setReportId(reportId);
executorService.submit(new ParallelApiExecTask(jMeterService, mapper, modeDataDTO, request.getConfig(), ApiRunMode.API_PLAN.name()));
}
}

View File

@ -8,6 +8,7 @@ import io.metersphere.api.dto.automation.RunModeConfig;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.base.domain.ApiDefinitionExecResult;
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil;
import org.apache.commons.lang3.StringUtils;
@ -44,10 +45,15 @@ public class SerialApiExecTask<T> implements Callable<T> {
Thread.sleep(3000);
index++;
report = mapper.selectByPrimaryKey(runModeDataDTO.getReportId());
if (report != null) {
if (report != null && !report.getStatus().equals(APITestStatus.Running.name())) {
break;
}
}
// 执行失败了恢复报告状态
if (index == 200 && report != null && report.getStatus().equals(APITestStatus.Running.name())) {
report.setStatus(APITestStatus.Error.name());
mapper.updateByPrimaryKey(report);
}
return (T) report;
} catch (Exception ex) {
LogUtil.error(ex.getMessage());

View File

@ -284,6 +284,7 @@ export default {
// environmentId: undefined,
currentCaseProjectId: "",
runData: [],
testPlanCaseIds: [],
reportId: "",
response: {},
rowLoading: "",
@ -531,6 +532,7 @@ export default {
return new Promise((resolve) => {
let index = 1;
this.runData = [];
this.testPlanCaseIds = [];
if (this.condition != null && this.condition.selectAll) {
let selectAllRowParams = buildBatchParam(this);
selectAllRowParams.ids = Array.from(this.selectRows).map(row => row.id);
@ -546,6 +548,7 @@ export default {
request.id = row.id;
request.useEnvironment = row.environmentId;
this.runData.unshift(request);
this.testPlanCaseIds.unshift(row.id);
if (dataRows.length === index) {
resolve();
}
@ -564,6 +567,7 @@ export default {
request.id = row.id;
request.useEnvironment = row.environmentId;
this.runData.unshift(request);
this.testPlanCaseIds.unshift(row.id);
if (this.selectRows.length === index) {
resolve();
}
@ -614,52 +618,11 @@ export default {
});
},
handleRunBatch(config) {
let testPlan = new TestPlan();
let projectId = getCurrentProjectID();
if (config.mode === 'serial') {
testPlan.serializeThreadgroups = true;
testPlan.hashTree = [];
this.runData.forEach(item => {
let threadGroup = new ThreadGroup();
threadGroup.onSampleError = config.onSampleError;
threadGroup.hashTree = [];
threadGroup.hashTree.push(item);
testPlan.hashTree.push(threadGroup);
});
let reqObj = {
id: getUUID().substring(0, 8),
testElement: testPlan,
type: 'API_PLAN',
reportId: "run",
projectId: projectId,
config: config
};
let bodyFiles = getBodyUploadFiles(reqObj, this.runData);
this.$fileUpload("/api/definition/run", null, bodyFiles, reqObj, response => {
this.$message('任务执行中,请稍后刷新查看结果');
this.$refs.taskCenter.open();
});
} else {
testPlan.serializeThreadgroups = false;
let threadGroup = new ThreadGroup();
threadGroup.hashTree = [];
testPlan.hashTree = [threadGroup];
this.runData.forEach(item => {
threadGroup.hashTree.push(item);
});
let reqObj = {
id: getUUID().substring(0, 8),
testElement: testPlan,
type: 'API_PLAN',
reportId: "run",
projectId: projectId
};
let bodyFiles = getBodyUploadFiles(reqObj, this.runData);
this.$fileUpload("/api/definition/run", null, bodyFiles, reqObj, response => {
this.$message('任务执行中,请稍后刷新查看结果');
this.$refs.taskCenter.open();
});
}
let obj = {planIds: this.testPlanCaseIds, config: config};
this.$post("/test/plan/api/case/run", obj, response => {
this.$message('任务执行中,请稍后刷新查看结果');
this.$refs.taskCenter.open();
});
this.search();
},
autoCheckStatus() { //