refactor(接口测试): 导入jmx文件优化,支持多线程组

--story=1011083 --user=赵勇 【Bug转需求】接口自动化支持导入多线程的jmx文件 https://www.tapd.cn/55049933/s/1329318
This commit is contained in:
fit2-zhao 2023-01-31 15:10:25 +08:00 committed by fit2-zhao
parent 273d8113b6
commit a2a2bd0571
2 changed files with 121 additions and 103 deletions

View File

@ -29,6 +29,7 @@ import io.metersphere.api.dto.definition.request.sampler.dubbo.MsConsumerAndServ
import io.metersphere.api.dto.definition.request.sampler.dubbo.MsRegistryCenter; import io.metersphere.api.dto.definition.request.sampler.dubbo.MsRegistryCenter;
import io.metersphere.api.dto.definition.request.timer.MsConstantTimer; import io.metersphere.api.dto.definition.request.timer.MsConstantTimer;
import io.metersphere.api.dto.definition.request.unknown.MsJmeterElement; import io.metersphere.api.dto.definition.request.unknown.MsJmeterElement;
import io.metersphere.api.dto.definition.request.variable.ScenarioVariable;
import io.metersphere.api.dto.scenario.Body; import io.metersphere.api.dto.scenario.Body;
import io.metersphere.api.dto.scenario.DatabaseConfig; import io.metersphere.api.dto.scenario.DatabaseConfig;
import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.KeyValue;
@ -37,10 +38,7 @@ import io.metersphere.api.parse.ApiImportAbstractParser;
import io.metersphere.base.domain.ApiScenarioWithBLOBs; import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import io.metersphere.base.domain.ApiTestEnvironmentExample; import io.metersphere.base.domain.ApiTestEnvironmentExample;
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
import io.metersphere.commons.constants.ElementConstants; import io.metersphere.commons.constants.*;
import io.metersphere.commons.constants.LoopConstants;
import io.metersphere.commons.constants.PropertyConstant;
import io.metersphere.commons.constants.RequestTypeConstants;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*; import io.metersphere.commons.utils.*;
import io.metersphere.environment.service.BaseEnvironmentService; import io.metersphere.environment.service.BaseEnvironmentService;
@ -48,8 +46,10 @@ import io.metersphere.plugin.core.MsTestElement;
import io.metersphere.request.BodyFile; import io.metersphere.request.BodyFile;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.assertions.*; import org.apache.jmeter.assertions.*;
import org.apache.jmeter.config.Argument;
import org.apache.jmeter.config.ConfigTestElement; import org.apache.jmeter.config.ConfigTestElement;
import org.apache.jmeter.control.ForeachController; import org.apache.jmeter.control.ForeachController;
import org.apache.jmeter.control.LoopController; import org.apache.jmeter.control.LoopController;
@ -75,6 +75,7 @@ import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestPlan; import org.apache.jmeter.testelement.TestPlan;
import org.apache.jmeter.testelement.property.JMeterProperty; import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.threads.ThreadGroup;
import org.apache.jmeter.timers.ConstantTimer; import org.apache.jmeter.timers.ConstantTimer;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
@ -105,7 +106,27 @@ public class JMeterParser extends ApiImportAbstractParser<ScenarioImport> {
MsScenario scenario = new MsScenario(); MsScenario scenario = new MsScenario();
scenario.setReferenced("IMPORT"); scenario.setReferenced("IMPORT");
formatHashTree(testPlan, scenario);
TestPlan plan = (TestPlan) testPlan.getArray()[0];
if (plan.getArguments() != null) {
List<ScenarioVariable> variables = new LinkedList<>();
plan.getArguments().forEach(item -> {
ScenarioVariable scenarioVariable = new ScenarioVariable();
scenarioVariable.setId(UUID.randomUUID().toString());
scenarioVariable.setName(item.getName());
if (ObjectUtils.isNotEmpty(item.getObjectValue())) {
Argument arg = (Argument) item.getObjectValue();
scenarioVariable.setValue(arg.getValue());
}
scenarioVariable.setType(VariableTypeConstants.CONSTANT.name());
variables.add(scenarioVariable);
});
scenario.setVariables(variables);
}
if (CollectionUtils.isEmpty(scenario.getHashTree())) {
scenario.setHashTree(new LinkedList<>());
}
formatHashTree(testPlan.getTree(plan), scenario);
this.projectId = request.getProjectId(); this.projectId = request.getProjectId();
ScenarioImport scenarioImport = new ScenarioImport(); ScenarioImport scenarioImport = new ScenarioImport();
scenarioImport.setData(parseObj(scenario, request)); scenarioImport.setData(parseObj(scenario, request));
@ -697,11 +718,7 @@ public class JMeterParser extends ApiImportAbstractParser<ScenarioImport> {
} }
// 测试计划 // 测试计划
if (key instanceof TestPlan) { if (key instanceof TestPlan) {
scenario.setName(((TestPlan) key).getName()); continue;
elementNode = new MsJmeterElement();
elementNode.setName(((TestPlan) key).getName());
((MsJmeterElement) elementNode).setJmeterElement(objToXml(key));
((MsJmeterElement) elementNode).setElementType(key.getClass().getSimpleName());
} }
// 线程组 // 线程组
else if (key instanceof ThreadGroup) { else if (key instanceof ThreadGroup) {

View File

@ -111,7 +111,7 @@
</el-col> </el-col>
<el-col :span="2" class="ms-col-one ms-font"> <el-col :span="2" class="ms-col-one ms-font">
<el-link class="head" @click="showScenarioParameters" <el-link class="head" @click="showScenarioParameters"
>{{ $t('api_test.automation.scenario_total') }} >{{ $t('api_test.automation.scenario_total') }}
</el-link> </el-link>
{{ getVariableSize() }} {{ getVariableSize() }}
</el-col> </el-col>
@ -152,10 +152,10 @@
size="mini" size="mini"
@command="handleCommand" @command="handleCommand"
v-permission="[ v-permission="[
'PROJECT_API_SCENARIO:READ+EDIT', 'PROJECT_API_SCENARIO:READ+EDIT',
'PROJECT_API_SCENARIO:READ+CREATE', 'PROJECT_API_SCENARIO:READ+CREATE',
'PROJECT_API_SCENARIO:READ+COPY', 'PROJECT_API_SCENARIO:READ+COPY',
]"> ]">
{{ $t('api_test.request.debug') }} {{ $t('api_test.request.debug') }}
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item>{{ $t('api_test.automation.generate_report') }}</el-dropdown-item> <el-dropdown-item>{{ $t('api_test.automation.generate_report') }}</el-dropdown-item>
@ -172,10 +172,10 @@
@click="editScenario" @click="editScenario"
title="ctrl + s" title="ctrl + s"
v-permission="[ v-permission="[
'PROJECT_API_SCENARIO:READ+EDIT', 'PROJECT_API_SCENARIO:READ+EDIT',
'PROJECT_API_SCENARIO:READ+CREATE', 'PROJECT_API_SCENARIO:READ+CREATE',
'PROJECT_API_SCENARIO:READ+COPY', 'PROJECT_API_SCENARIO:READ+COPY',
]"> ]">
{{ $t('commons.save') }} {{ $t('commons.save') }}
</el-button> </el-button>
@ -196,26 +196,26 @@
<i <i
class="el-icon-star-off" class="el-icon-star-off"
style=" style="
color: var(--primary_color); color: var(--primary_color);
font-size: 22px; font-size: 22px;
margin-right: 5px; margin-right: 5px;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
top: 3px; top: 3px;
" "
@click="saveFollow" /> @click="saveFollow" />
</el-tooltip> </el-tooltip>
<el-tooltip :content="$t('commons.cancel')" placement="bottom" effect="dark" v-show="showFollow"> <el-tooltip :content="$t('commons.cancel')" placement="bottom" effect="dark" v-show="showFollow">
<i <i
class="el-icon-star-on" class="el-icon-star-on"
style=" style="
color: var(--primary_color); color: var(--primary_color);
font-size: 22px; font-size: 22px;
margin-right: 5px; margin-right: 5px;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
top: 3px; top: 3px;
" "
@click="saveFollow" /> @click="saveFollow" />
</el-tooltip> </el-tooltip>
<el-link <el-link
@ -223,7 +223,7 @@
style="margin-right: 5px" style="margin-right: 5px"
@click="openHis" @click="openHis"
v-show="path === '/api/automation/update'" v-show="path === '/api/automation/update'"
>{{ $t('operating_log.change_history') }} >{{ $t('operating_log.change_history') }}
</el-link> </el-link>
<!-- 版本历史 --> <!-- 版本历史 -->
<mx-version-history <mx-version-history
@ -258,8 +258,8 @@
<span class="ms-message-right">{{ $t('api_test.automation.request_total') }} {{ reqTotal }}</span> <span class="ms-message-right">{{ $t('api_test.automation.request_total') }} {{ reqTotal }}</span>
<span class="ms-message-right">{{ $t('api_test.automation.request_success') }} {{ reqSuccess }}</span> <span class="ms-message-right">{{ $t('api_test.automation.request_success') }} {{ reqSuccess }}</span>
<span class="ms-message-right"> <span class="ms-message-right">
{{ $t('api_test.automation.request_error') }} {{ $t('api_test.automation.request_error') }}
{{ reqError }}</span {{ reqError }}</span
> >
</div> </div>
</el-col> </el-col>
@ -293,65 +293,65 @@
align="middle" align="middle"
slot-scope="{ node, data }" slot-scope="{ node, data }"
style="width: 100%"> style="width: 100%">
<span
class="custom-tree-node-col"
style="padding-left: 0px; padding-right: 0px"
v-show="node && data.hashTree && data.hashTree.length > 0 && !data.isLeaf">
<span <span
v-show="!node.expanded" class="custom-tree-node-col"
class="el-icon-circle-plus-outline custom-node_e" style="padding-left: 0px; padding-right: 0px"
@click="openOrClose(node, data)" /> v-show="node && data.hashTree && data.hashTree.length > 0 && !data.isLeaf">
<span <span
v-show="node.expanded" v-show="!node.expanded"
class="el-icon-remove-outline custom-node_e" class="el-icon-circle-plus-outline custom-node_e"
@click="openOrClose(node, data)" /> @click="openOrClose(node, data)" />
</span> <span
v-show="node.expanded"
class="el-icon-remove-outline custom-node_e"
@click="openOrClose(node, data)" />
</span>
<!-- 批量操作 --> <!-- 批量操作 -->
<span <span
:class="data.checkBox ? 'custom-tree-node-hide' : 'custom-tree-node-col'" :class="data.checkBox ? 'custom-tree-node-hide' : 'custom-tree-node-col'"
style="padding-left: 0px; padding-right: 0px" style="padding-left: 0px; padding-right: 0px"
v-show="(data.hashTree && data.hashTree.length === 0) || data.isLeaf"> v-show="(data.hashTree && data.hashTree.length === 0) || data.isLeaf">
<show-more-btn <show-more-btn
:is-show="node.checked" :is-show="node.checked"
:buttons="batchOperators" :buttons="batchOperators"
:size="selectDataCounts" :size="selectDataCounts"
v-show="data.checkBox" v-show="data.checkBox"
style="margin-right: 10px" /> style="margin-right: 10px" />
</span> </span>
<span style="width: calc(100% - 40px)"> <span style="width: calc(100% - 40px)">
<!-- 步骤组件--> <!-- 步骤组件-->
<ms-component-config <ms-component-config
:scenario-definition="scenarioDefinition" :scenario-definition="scenarioDefinition"
:message="message" :message="message"
:type="data.type" :type="data.type"
:scenario="data" :scenario="data"
:response="response" :response="response"
:currentScenario="currentScenario" :currentScenario="currentScenario"
:node="node" :node="node"
:project-list="projectList" :project-list="projectList"
:env-map="projectEnvMap" :env-map="projectEnvMap"
:env-group-id="envGroupId" :env-group-id="envGroupId"
:environment-type="environmentType" :environment-type="environmentType"
@remove="remove" @remove="remove"
@copyRow="copyRow" @copyRow="copyRow"
@suggestClick="suggestClick" @suggestClick="suggestClick"
@refReload="refReload" @refReload="refReload"
@runScenario="runDebug" @runScenario="runDebug"
@stopScenario="stop" @stopScenario="stop"
@setDomain="setDomain" @setDomain="setDomain"
@openScenario="openScenario" @openScenario="openScenario"
@editScenarioAdvance="editScenarioAdvance" @editScenarioAdvance="editScenarioAdvance"
ref="componentConfig" ref="componentConfig"
v-if=" v-if="
stepFilter.get('ALlSamplerStep').indexOf(data.type) === -1 || stepFilter.get('ALlSamplerStep').indexOf(data.type) === -1 ||
!node.parent || !node.parent ||
!node.parent.data || !node.parent.data ||
stepFilter.get('AllSamplerProxy').indexOf(node.parent.data.type) === -1 stepFilter.get('AllSamplerProxy').indexOf(node.parent.data.type) === -1
" /> " />
<div v-else class="el-tree-node is-hidden is-focusable is-leaf" style="display: none"> <div v-else class="el-tree-node is-hidden is-focusable is-leaf" style="display: none">
{{ hideNode(node) }} {{ hideNode(node) }}
</div> </div>
</span> </span>
</el-row> </el-row>
</el-tree> </el-tree>
</div> </div>
@ -576,12 +576,11 @@ import {
saveScenario, saveScenario,
} from '@/business/automation/api-automation'; } from '@/business/automation/api-automation';
import MsComponentConfig from './component/ComponentConfig'; import MsComponentConfig from './component/ComponentConfig';
import {ENV_TYPE} from 'metersphere-frontend/src/utils/constants'; import { ENV_TYPE } from 'metersphere-frontend/src/utils/constants';
import {mergeRequestDocumentData} from '@/business/definition/api-definition'; import { mergeRequestDocumentData } from '@/business/definition/api-definition';
import {getEnvironmentByProjectId} from 'metersphere-frontend/src/api/environment'; import { getEnvironmentByProjectId } from 'metersphere-frontend/src/api/environment';
import {useApiStore} from '@/store'; import { useApiStore } from '@/store';
import {getDefaultVersion, setLatestVersionById} from 'metersphere-frontend/src/api/version'; import { getDefaultVersion, setLatestVersionById } from 'metersphere-frontend/src/api/version';
const store = useApiStore(); const store = useApiStore();
@ -790,7 +789,7 @@ export default {
debugReportId: '', debugReportId: '',
isPreventReClick: false, isPreventReClick: false,
latestVersionId: '', latestVersionId: '',
hasLatest: false hasLatest: false,
}; };
}, },
created() { created() {
@ -923,6 +922,9 @@ export default {
if (!array) { if (!array) {
array = this.scenarioDefinition; array = this.scenarioDefinition;
} }
if (!array) {
return;
}
let isLeaf = true; let isLeaf = true;
let nodeType = ''; let nodeType = '';
if (node) { if (node) {
@ -2489,11 +2491,10 @@ export default {
if (!hasLicense()) { if (!hasLicense()) {
return; return;
} }
getDefaultVersion(this.projectId) getDefaultVersion(this.projectId).then((response) => {
.then(response => { this.latestVersionId = response.data;
this.latestVersionId = response.data; this.getVersionHistory();
this.getVersionHistory(); });
});
}, },
getVersionHistory() { getVersionHistory() {
if (!hasLicense()) { if (!hasLicense()) {
@ -2507,7 +2508,7 @@ export default {
} }
let latestVersionData = response.data.filter((v) => v.versionId === this.latestVersionId); let latestVersionData = response.data.filter((v) => v.versionId === this.latestVersionId);
if (latestVersionData.length > 0) { if (latestVersionData.length > 0) {
this.hasLatest = false this.hasLatest = false;
} else { } else {
this.hasLatest = true; this.hasLatest = true;
} }
@ -2567,8 +2568,8 @@ export default {
projectId: this.projectId, projectId: this.projectId,
type: 'SCENARIO', type: 'SCENARIO',
versionId: row.id, versionId: row.id,
resourceId: this.currentScenario.id resourceId: this.currentScenario.id,
} };
setLatestVersionById(param).then(() => { setLatestVersionById(param).then(() => {
this.$success(this.$t('commons.modify_success')); this.$success(this.$t('commons.modify_success'));
this.checkout(row); this.checkout(row);