feat(接口测试): 增加场景级别断言配置

This commit is contained in:
Captain.B 2020-11-11 12:08:22 +08:00
parent d772fac5e3
commit 6b443008cd
5 changed files with 149 additions and 124 deletions

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.scenario;
import io.metersphere.api.dto.scenario.assertions.Assertions;
import io.metersphere.api.dto.scenario.request.Request;
import lombok.Data;
@ -15,6 +16,7 @@ public class Scenario {
private List<KeyValue> variables;
private List<KeyValue> headers;
private List<Request> requests;
private Assertions assertions;
private DubboConfig dubboConfig;
private TCPConfig tcpConfig;
private List<DatabaseConfig> databaseConfigs;

View File

@ -76,28 +76,28 @@
</template>
<script>
import MsApiScenarioConfig from "./components/ApiScenarioConfig";
import {Scenario, Test} from "./model/ScenarioModel"
import MsApiReportStatus from "../report/ApiReportStatus";
import MsApiReportDialog from "./ApiReportDialog";
import {checkoutTestManagerOrTestUser, downloadFile, getUUID} from "@/common/js/utils";
import MsScheduleConfig from "../../common/components/MsScheduleConfig";
import ApiImport from "./components/import/ApiImport";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
import MsContainer from "@/business/components/common/components/MsContainer";
import MsMainContainer from "@/business/components/common/components/MsMainContainer";
import MsJarConfig from "./components/jar/JarConfig";
import MsApiScenarioConfig from "./components/ApiScenarioConfig";
import {Scenario, Test} from "./model/ScenarioModel"
import MsApiReportStatus from "../report/ApiReportStatus";
import MsApiReportDialog from "./ApiReportDialog";
import {checkoutTestManagerOrTestUser, downloadFile, getUUID} from "@/common/js/utils";
import MsScheduleConfig from "../../common/components/MsScheduleConfig";
import ApiImport from "./components/import/ApiImport";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
import MsContainer from "@/business/components/common/components/MsContainer";
import MsMainContainer from "@/business/components/common/components/MsMainContainer";
import MsJarConfig from "./components/jar/JarConfig";
export default {
name: "MsApiTestConfig",
export default {
name: "MsApiTestConfig",
components: {
MsJarConfig,
MsMainContainer,
MsContainer, ApiImport, MsScheduleConfig, MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig
},
components: {
MsJarConfig,
MsMainContainer,
MsContainer, ApiImport, MsScheduleConfig, MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig
},
props: ["id"],
props: ["id"],
data() {
return {
@ -305,6 +305,7 @@
},
cancel() {
this.$router.push('/api/test/list/all');
// console.log(this.test.toJMX().xml);
},
handleCommand(command) {
switch (command) {

View File

@ -52,6 +52,9 @@
<el-tab-pane :label="$t('api_test.environment.tcp_config')" name="tcp">
<ms-tcp-config :config="scenario.tcpConfig" :is-read-only="isReadOnly"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.assertions.label')" name="assertions">
<ms-api-assertions :scenario="scenario" :is-read-only="isReadOnly" :assertions="scenario.assertions"/>
</el-tab-pane>
</el-tabs>
<api-environment-config ref="environmentConfig" @close="environmentConfigClose"/>
@ -72,6 +75,7 @@ import MsDubboConsumerService from "@/business/components/api/test/components/re
import MsDatabaseConfig from "./request/database/DatabaseConfig";
import {parseEnvironment} from "../model/EnvironmentModel";
import MsTcpConfig from "@/business/components/api/test/components/request/tcp/TcpConfig";
import MsApiAssertions from "@/business/components/api/test/components/assertion/ApiAssertions";
export default {
name: "MsApiScenarioForm",
@ -79,7 +83,8 @@ export default {
MsTcpConfig,
MsDatabaseConfig,
MsDubboConsumerService,
MsDubboConfigCenter, MsDubboRegistryCenter, ApiEnvironmentConfig, MsApiScenarioVariables, MsApiKeyValue
MsDubboConfigCenter, MsDubboRegistryCenter, ApiEnvironmentConfig, MsApiScenarioVariables, MsApiKeyValue,
MsApiAssertions
},
props: {
scenario: Scenario,

View File

@ -3,7 +3,8 @@
<div class="assertion-add">
<el-row :gutter="10">
<el-col :span="4">
<el-select :disabled="isReadOnly" class="assertion-item" v-model="type" :placeholder="$t('api_test.request.assertions.select_type')"
<el-select :disabled="isReadOnly" class="assertion-item" v-model="type"
:placeholder="$t('api_test.request.assertions.select_type')"
size="small">
<el-option :label="$t('api_test.request.assertions.text')" :value="options.TEXT"/>
<el-option :label="$t('api_test.request.assertions.regex')" :value="options.REGEX"/>
@ -14,13 +15,18 @@
</el-select>
</el-col>
<el-col :span="20">
<ms-api-assertion-text :is-read-only="isReadOnly" :list="assertions.regex" v-if="type === options.TEXT" :callback="after"/>
<ms-api-assertion-regex :is-read-only="isReadOnly" :list="assertions.regex" v-if="type === options.REGEX" :callback="after"/>
<ms-api-assertion-json-path :is-read-only="isReadOnly" :list="assertions.jsonPath" v-if="type === options.JSON_PATH" :callback="after"/>
<ms-api-assertion-x-path2 :is-read-only="isReadOnly" :list="assertions.xpath2" v-if="type === options.XPATH2" :callback="after"/>
<ms-api-assertion-text :is-read-only="isReadOnly" :list="assertions.regex" v-if="type === options.TEXT"
:callback="after"/>
<ms-api-assertion-regex :is-read-only="isReadOnly" :list="assertions.regex" v-if="type === options.REGEX"
:callback="after"/>
<ms-api-assertion-json-path :is-read-only="isReadOnly" :list="assertions.jsonPath"
v-if="type === options.JSON_PATH" :callback="after"/>
<ms-api-assertion-x-path2 :is-read-only="isReadOnly" :list="assertions.xpath2" v-if="type === options.XPATH2"
:callback="after"/>
<ms-api-assertion-duration :is-read-only="isReadOnly" v-model="time" :duration="assertions.duration"
v-if="type === options.DURATION" :callback="after"/>
<ms-api-assertion-jsr223 :is-read-only="isReadOnly" :list="assertions.jsr223" v-if="type === options.JSR223" :callback="after"/>
<ms-api-assertion-jsr223 :is-read-only="isReadOnly" :list="assertions.jsr223" v-if="type === options.JSR223"
:callback="after"/>
<el-button v-if="!type" :disabled="true" type="primary" size="small">
{{ $t('api_test.request.assertions.add') }}
</el-button>
@ -28,125 +34,128 @@
</el-row>
</div>
<div>
<div v-if="!scenario">
<el-row :gutter="10" class="json-path-suggest-button">
<el-button size="small" type="primary" @click="suggestJsonOpen">
{{$t('api_test.request.assertions.json_path_suggest')}}
</el-button>
<el-button size="small" type="danger" @click="clearJson">
{{$t('api_test.request.assertions.json_path_clear')}}
</el-button>
<el-button size="small" type="primary" @click="suggestJsonOpen">
{{ $t('api_test.request.assertions.json_path_suggest') }}
</el-button>
<el-button size="small" type="danger" @click="clearJson">
{{ $t('api_test.request.assertions.json_path_clear') }}
</el-button>
</el-row>
</div>
<ms-api-jsonpath-suggest-list @addJsonpathSuggest="addJsonpathSuggest" :request="request" ref="jsonpathSuggestList"/>
<ms-api-jsonpath-suggest-list @addJsonpathSuggest="addJsonpathSuggest" :request="request"
ref="jsonpathSuggestList"/>
<ms-api-assertions-edit :is-read-only="isReadOnly" :assertions="assertions"/>
</div>
</template>
<script>
import MsApiAssertionText from "./ApiAssertionText";
import MsApiAssertionRegex from "./ApiAssertionRegex";
import MsApiAssertionDuration from "./ApiAssertionDuration";
import {ASSERTION_TYPE, Assertions, HttpRequest, JSONPath} from "../../model/ScenarioModel";
import MsApiAssertionsEdit from "./ApiAssertionsEdit";
import MsApiAssertionJsonPath from "./ApiAssertionJsonPath";
import MsApiAssertionJsr223 from "@/business/components/api/test/components/assertion/ApiAssertionJsr223";
import MsApiJsonpathSuggestList from "./ApiJsonpathSuggestList";
import MsApiAssertionXPath2 from "./ApiAssertionXPath2";
import MsApiAssertionText from "./ApiAssertionText";
import MsApiAssertionRegex from "./ApiAssertionRegex";
import MsApiAssertionDuration from "./ApiAssertionDuration";
import {ASSERTION_TYPE, Assertions, HttpRequest, JSONPath, Scenario} from "../../model/ScenarioModel";
import MsApiAssertionsEdit from "./ApiAssertionsEdit";
import MsApiAssertionJsonPath from "./ApiAssertionJsonPath";
import MsApiAssertionJsr223 from "@/business/components/api/test/components/assertion/ApiAssertionJsr223";
import MsApiJsonpathSuggestList from "./ApiJsonpathSuggestList";
import MsApiAssertionXPath2 from "./ApiAssertionXPath2";
export default {
name: "MsApiAssertions",
export default {
name: "MsApiAssertions",
components: {
MsApiAssertionXPath2,
MsApiAssertionJsr223,
MsApiJsonpathSuggestList,
MsApiAssertionJsonPath,
MsApiAssertionsEdit, MsApiAssertionDuration, MsApiAssertionRegex, MsApiAssertionText},
components: {
MsApiAssertionXPath2,
MsApiAssertionJsr223,
MsApiJsonpathSuggestList,
MsApiAssertionJsonPath,
MsApiAssertionsEdit, MsApiAssertionDuration, MsApiAssertionRegex, MsApiAssertionText
},
props: {
assertions: Assertions,
request: HttpRequest,
isReadOnly: {
type: Boolean,
default: false
}
},
data() {
return {
options: ASSERTION_TYPE,
time: "",
type: "",
}
},
methods: {
after() {
this.type = "";
},
suggestJsonOpen() {
if (!this.request.debugRequestResult) {
this.$message(this.$t('api_test.request.assertions.debug_first'));
return;
}
this.$refs.jsonpathSuggestList.open();
},
addJsonpathSuggest(jsonPathList) {
jsonPathList.forEach(jsonPath => {
let jsonItem = new JSONPath();
jsonItem.expression = jsonPath.json_path;
jsonItem.expect = jsonPath.json_value;
jsonItem.setJSONPathDescription();
this.assertions.jsonPath.push(jsonItem);
});
},
clearJson() {
this.assertions.jsonPath = [];
}
props: {
assertions: Assertions,
request: HttpRequest,
scenario: Scenario,
isReadOnly: {
type: Boolean,
default: false
}
},
data() {
return {
options: ASSERTION_TYPE,
time: "",
type: "",
}
},
methods: {
after() {
this.type = "";
},
suggestJsonOpen() {
if (!this.request.debugRequestResult) {
this.$message(this.$t('api_test.request.assertions.debug_first'));
return;
}
this.$refs.jsonpathSuggestList.open();
},
addJsonpathSuggest(jsonPathList) {
jsonPathList.forEach(jsonPath => {
let jsonItem = new JSONPath();
jsonItem.expression = jsonPath.json_path;
jsonItem.expect = jsonPath.json_value;
jsonItem.setJSONPathDescription();
this.assertions.jsonPath.push(jsonItem);
});
},
clearJson() {
this.assertions.jsonPath = [];
}
}
}
</script>
<style scoped>
.assertion-item {
width: 100%;
}
.assertion-item {
width: 100%;
}
.assertion-add {
padding: 10px;
border: #DCDFE6 solid 1px;
margin: 5px 0;
border-radius: 5px;
}
.assertion-add {
padding: 10px;
border: #DCDFE6 solid 1px;
margin: 5px 0;
border-radius: 5px;
}
.bg-purple-dark {
background: #99a9bf;
}
.bg-purple-dark {
background: #99a9bf;
}
.bg-purple {
background: #d3dce6;
}
.bg-purple {
background: #d3dce6;
}
.bg-purple-light {
background: #e5e9f2;
}
.bg-purple-light {
background: #e5e9f2;
}
.grid-content {
border-radius: 4px;
min-height: 36px;
}
.grid-content {
border-radius: 4px;
min-height: 36px;
}
.row-bg {
padding: 10px 0;
background-color: #f9fafc;
}
.row-bg {
padding: 10px 0;
background-color: #f9fafc;
}
.json-path-suggest-button {
text-align: right;
}
.json-path-suggest-button {
text-align: right;
}
</style>

View File

@ -1,5 +1,6 @@
import {
Arguments,
ConstantTimer as JMXConstantTimer,
CookieManager,
DNSCacheManager,
DubboSample,
@ -10,22 +11,24 @@ import {
HTTPSamplerArguments,
HTTPsamplerFiles,
HTTPSamplerProxy,
IfController as JMXIfController,
JDBCDataSource,
JDBCSampler,
JSONPathAssertion,
JSONPostProcessor,
JSR223Assertion,
JSR223PostProcessor,
JSR223PreProcessor,
RegexExtractor,
ResponseCodeAssertion,
ResponseDataAssertion,
ResponseHeadersAssertion,
TCPSampler,
TestElement,
TestPlan,
ThreadGroup,
XPath2Assertion,
XPath2Extractor,
IfController as JMXIfController,
ConstantTimer as JMXConstantTimer, TCPSampler, JSR223Assertion, XPath2Assertion,
} from "./JMX";
import Mock from "mockjs";
import {funcFilters} from "@/common/js/func-filter";
@ -226,6 +229,7 @@ export class Scenario extends BaseConfig {
this.enable = true;
this.databaseConfigs = [];
this.tcpConfig = undefined;
this.assertions = undefined;
this.set(options);
this.sets({
@ -242,6 +246,7 @@ export class Scenario extends BaseConfig {
options.databaseConfigs = options.databaseConfigs || [];
options.dubboConfig = new DubboConfig(options.dubboConfig);
options.tcpConfig = new TCPConfig(options.tcpConfig);
options.assertions = new Assertions(options.assertions);
return options;
}
@ -1151,6 +1156,9 @@ class JMXGenerator {
this.addScenarioCookieManager(threadGroup, scenario);
this.addJDBCDataSources(threadGroup, scenario);
this.addAssertion(threadGroup, scenario);
scenario.requests.forEach(request => {
if (request.enable) {
if (!request.isValid()) return;
@ -1175,7 +1183,7 @@ class JMXGenerator {
this.addRequestExtractor(sampler, request);
this.addRequestAssertion(sampler, request);
this.addAssertion(sampler, request);
this.addJSR223PreProcessor(sampler, request);
@ -1467,7 +1475,7 @@ class JMXGenerator {
httpSamplerProxy.add(new HTTPsamplerFiles(files));
}
addRequestAssertion(httpSamplerProxy, request) {
addAssertion(httpSamplerProxy, request) {
let assertions = request.assertions;
if (assertions.regex.length > 0) {
assertions.regex.filter(this.filter).forEach(regex => {