add: 性能测试-报告

This commit is contained in:
2025-04-21 10:54:16 +08:00
committed by 温若烈酒慕茶凉丷
parent f677776c31
commit 97e74dd4ca
6 changed files with 220 additions and 66 deletions

View File

@@ -68,4 +68,12 @@ export function editAndExecuteTest(data) {
method: 'put',
data: data
})
}
}
// 测试-编辑-立即执行
export function executeTest(query) {
return request({
url: '/test/performanceTest/executeNow?' + query,
method: 'get',
})
}

View File

@@ -1 +1,26 @@
import request from '@/utils/request'
import request from '@/utils/request'
// 报告-列表查询
export function getReportList(query) {
return request({
url: '/test/performanceReport//list',
method: 'get',
params: query
})
}
// 报告-列表删除
export function deleteReport(data) {
return request({
url: '/test/performanceReport/delete/' + data,
method: 'put',
data: data
})
}
// 报告-详情
export function getReportDetail(query) {
return request({
url: '/test/performanceReport/getInfo/' + query,
method: 'get',
})
}

View File

@@ -148,7 +148,7 @@ export default {
addForm: {
performanceName: '', // 性能测试名称
performanceTestCaseVOList: [], // 场景列表
concurrentThreads: '', // 并发用户数
concurrentThreads: '0', // 并发用户数
errorOperType: '', // 在取样器错误后要执行的动作1继续2开始下一个线程轮询3停止线程4停止测试5立即停止测试
executeType: '', // 执行方式1按持续时间2按迭代次数
rampUpSeconds: '0', // 多少秒内线程建立完成默认0
@@ -193,14 +193,36 @@ export default {
time: "2025-02-22 10:00:00",
},
],
validation: false, // 校验
}
},
mounted() {
this.getTestCaseData()
},
methods: {
// 校验
validationForm() {
if (this.addForm.performanceName === '' || this.addForm.performanceName === null) {
this.$message({ message: '请输入测试名称', type: 'warning' })
return
}
if (this.addForm.errorOperType === '' || this.addForm.errorOperType === null) {
this.$message({ message: '请填写取样器错误后', type: 'warning' })
return
}
if (this.addForm.executeType === '' || this.addForm.executeType === null) {
this.$message({ message: '请选择执行方式', type: 'warning' })
return
}
this.validation = true
},
// 保存
handleWithSave() {
this.validationForm()
if (this.validation === false) { return }
this.changeList.forEach(item => {
var par = {
testCaseId: item.id,
@@ -208,7 +230,6 @@ export default {
}
this.addForm.performanceTestCaseVOList.push(par)
})
console.log(this.addForm)
addTest(this.addForm).then(res => {
if (res.code === 200) {
this.$message({ message: '新增成功', type: 'success' })
@@ -220,6 +241,9 @@ export default {
},
// 保存并执行
handleWithSaveAndExecute() {
this.validationForm()
if (this.validation === false) { return }
// 需要校验crontab表达式
addAndExecuteTest(this.addForm).then(res => {
if (res.code === 200) {
this.$message({ message: '新增成功', type: 'success' })

View File

@@ -8,6 +8,7 @@
<div class="save">
<el-button type="primary" plain @click="handleWithSave">保存</el-button>
<el-button type="primary" plain @click="handleWithSaveAndExecute">保存并执行</el-button>
<el-button type="primary" plain @click="handleWithExecute">立即执行</el-button>
<el-button type="warning" plain @click="handleWithCancel">取消</el-button>
</div>
<div class="date">
@@ -143,7 +144,7 @@
</template>
<script>
import { editTest, editAndExecuteTest, getTestCaseList, getTestDetail } from '../../../api/performance';
import { editTest, editAndExecuteTest, getTestCaseList, getTestDetail, executeTest } from '../../../api/performance';
export default {
name: "PerformanceEdit",
@@ -233,6 +234,17 @@ export default {
}
})
},
// 立即执行
handleWithExecute() {
executeTest(this.$route.query.id).then(res => {
if (res.code === 200) {
this.$message({ message: '执行成功', type: 'success' })
this.$tab.closeOpenPage({ path: "/performance/performance" });
} else {
this.$message({ message: '执行失败', type: 'error' })
}
})
},
// 取消
handleWithCancel() {
this.$tab.closeOpenPage({ path: "/performance/performance" });

View File

@@ -9,16 +9,24 @@
<div class="table">
<el-table :data="data">
<el-table-column type="selection" width="55" />
<el-table-column prop="name0" label="名称" align="center" sortable />
<el-table-column prop="name1" label="创建人" align="center" width="150" sortable />
<el-table-column prop="name2" label="并发数" align="center" width="150" sortable />
<el-table-column prop="name3" label="响应(s)" align="center" width="150" sortable />
<el-table-column prop="name4" label="TPS" align="center" width="150" sortable />
<el-table-column prop="name5" label="开始时间" align="center" width="150" sortable />
<el-table-column prop="name6" label="结束时间" align="center" width="150" sortable />
<el-table-column prop="name7" label="执行时长" align="center" width="150" sortable />
<el-table-column prop="name8" label="触发方式" align="center" width="150" sortable />
<el-table-column prop="name9" label="状态" align="center" width="150" sortable />
<el-table-column prop="name" label="名称" align="center" sortable width="150" />
<el-table-column prop="createBy" label="创建人" align="center" width="150" sortable />
<el-table-column prop="concurrentThreads" label="并发数" align="center" width="150" sortable />
<el-table-column prop="tps" label="TPS" align="center" width="150" sortable />
<el-table-column prop="startTime" label="开始时间" align="center" sortable />
<el-table-column prop="endTime" label="结束时间" align="center" sortable />
<el-table-column prop="triggerType" label="触发方式" align="center" sortable>
<template slot-scope="scope">
<div v-if="scope.row.triggerType === 1">按持续时间</div>
<div v-else>按迭代次数</div>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" align="center" width="150" sortable>
<template slot-scope="scope">
<div v-if="scope.row.triggerType === 1">开启</div>
<div v-else>关闭</div>
</template>
</el-table-column>
<el-table-column prop="action" label="操作" align="center" width="150" fixed="right">
<template slot-scope="scope">
<el-button type="text" icon="el-icon-view" @click="hadleClickDetail(scope.row)">查看</el-button>
@@ -36,6 +44,8 @@
</template>
<script>
import { getReportList, deleteReport } from '../../../api/performance/report';
export default {
name: "Report",
data() {
@@ -46,24 +56,41 @@ export default {
performanceName: '', // 名称
},
total: 0,
data: [{}, {}],
data: [],
}
},
mounted() {
this.getReportListData()
},
methods: {
// 查看
hadleClickDetail() {
this.$tab.openPage("报告详情", "/performance/report/detail");
hadleClickDetail(val) {
this.$tab.openPage("报告详情", "/performance/report/detail", { id: val.id });
},
// 删除
hadleClickDelete() {
// 删除
hadleClickDelete(val) {
deleteReport(val.id).then(res => {
if (res.code === 200) {
this.$message({ message: '删除成功', type: 'success' })
this.getReportListData()
}
})
},
// 获取列表数据
getReportListData() {
getReportList(this.serachForm).then(res => {
if (res.code === 200) {
this.data = res.rows
this.total = res.total
}
})
},
// 分页
handleSizeChange() { },
handleCurrentChange() { },
serachList() { },
serachList() {
this.getReportListData()
},
}
}

View File

@@ -1,63 +1,85 @@
<template>
<div class="report-detail">
<div class="header">
<div class="title">执行时长</div>
<div class="title">开始时间</div>
<div class="title">结束时间</div>
<div class="title">执行时长{{ detailData ? detailData.costTime : '' }}</div>
<div class="title">开始时间{{ detailData ? detailData.startTime : '' }}</div>
<div class="title">结束时间{{ detailData ? detailData.endTime : ' ' }}</div>
</div>
<div class="tabs">
<el-tabs v-model="activeName">
<el-tab-pane label="请求统计" name="first">
<div class="btn-wrap">
<el-button @click="exportExcel">导出为Excel</el-button>
</div>
<el-table :data="statisticsData">
<el-table-column label="Requests" align="center">
<el-table-column prop="name" label="Label" width="120" sortable align="center" />
<el-table-column prop="label" label="Label" sortable align="center" />
</el-table-column>
<el-table-column label="Executions" align="center">
<el-table-column prop="name" label="Samples" width="120" sortable align="center" />
<el-table-column prop="name" label="FAIL" width="120" sortable align="center" />
<el-table-column prop="name" label="Error%" width="120" sortable align="center" />
<el-table-column prop="samples" label="Samples" sortable align="center" />
</el-table-column>
<el-table-column label="Response Times(ms)" align="center">
<el-table-column prop="name" label="Avg" width="120" sortable align="center" />
<el-table-column prop="name" label="Min" width="120" sortable align="center" />
<el-table-column prop="name" label="Max" width="120" sortable align="center" />
<el-table-column prop="name" label="Med" width="120" sortable align="center" />
<el-table-column prop="name" label="90%" width="120" sortable align="center" />
<el-table-column prop="name" label="95%" width="120" sortable align="center" />
<el-table-column prop="name" label="99%" width="120" sortable align="center" />
<el-table-column prop="average" label="Avg" sortable align="center" />
<el-table-column prop="min" label="Min" sortable align="center" />
<el-table-column prop="max" label="Max" sortable align="center" />
<el-table-column prop="errorRate" label="ErrorRate" sortable align="center">
<template slot-scope="scope">
<div>{{ getFormatToTwoDecimalPlaces(scope.row.errorRate) }}%</div>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="Throughput" align="center">
<el-table-column prop="name" label="Trans/s" width="120" sortable align="center" />
<el-table-column prop="throughput" label="Trans/s" width="120" sortable align="center">
<template slot-scope="scope">
<div>{{ getFormatToTwoDecimalPlaces(scope.row.throughput) }}%</div>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="NetWork(KB/Sec)" align="center">
<el-table-column prop="name" label="Recd" width="120" sortable align="center" />
<el-table-column prop="name" label="Sent" width="120" sortable align="center" />
<el-table-column prop="receivedKBPerSec" label="Recd" width="120" sortable align="center">
<template slot-scope="scope">
<div>{{ getFormatToTwoDecimalPlaces(scope.row.receivedKBPerSec) }}%</div>
</template>
</el-table-column>
<el-table-column prop="sentKBPerSec" label="Sent" width="120" sortable align="center">
<template slot-scope="scope">
<div>{{ getFormatToTwoDecimalPlaces(scope.row.sentKBPerSec) }}%</div>
</template>
</el-table-column>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="错误记录" name="second">
<div class="title-error">Errors</div>
<el-table :data="errorList" border>
<el-table-column prop="date" label="Type of error" sortable />
<el-table-column prop="name" label="Number of errors" sortable />
<el-table-column prop="address" label="% in errors" sortable />
<el-table-column prop="address" label="% in samples" sortable />
<el-table :data="errorCodeStatList" border>
<el-table-column prop="responseCode" label="Type of error" sortable align="center" />
<el-table-column prop="count" label="Number of errors" sortable align="center" />
<el-table-column prop="errorRatio" label="% in errors" sortable align="center">
<template slot-scope="scope">
<div>{{ getFormatToTwoDecimalPlaces(scope.row.errorRatio) }}%</div>
</template>
</el-table-column>
<el-table-column prop="totalRatio" label="% in samples" sortable align="center">
<template slot-scope="scope">
<div>{{ getFormatToTwoDecimalPlaces(scope.row.totalRatio) }}%</div>
</template>
</el-table-column>
</el-table>
<div class="title-error">Top 5 Errors</div>
<el-table :data="errorList" border>
<el-table-column prop="date" label="Sample" sortable />
<el-table-column prop="name" label="#Samples" sortable />
<el-table-column prop="address" label="All Errors" sortable />
</el-table>
<div class="title-error">#1 Errors</div>
<el-table :data="errorList" border>
<el-table-column prop="date" label="Sample" sortable />
<el-table-column prop="name" label="#1 Error" sortable />
<el-table-column prop="address" label="#1 Error Count" sortable />
<el-table :data="top5InterfaceErrorStatList" border>
<el-table-column prop="interfaceName" label="Sample" sortable align="center" />
<el-table-column prop="errorCount" label="#Samples" sortable align="center" />
<el-table-column prop="totalSamples" label="All Errors" sortable align="center" />
</el-table>
<div v-for="(item, index) in interfaceErrorDetailList">
<div class="title-error">#{{ index + 1 }} Errors</div>
<el-table :data="interfaceErrorDetailList" border>
<el-table-column prop="interfaceName" label="Sample" sortable align="center" />
<el-table-column prop="errorName" :label="getLabel(index)" sortable align="center">
<template slot-scope="scope">{{ getErrorName(scope.row) }}</template>
</el-table-column>
<el-table-column prop="errorCount" :label="getErrorCount(index)" sortable align="center">
<template slot-scope="scope">{{ getErrorCountDetail(scope.row) }}</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
</el-tabs>
</div>
@@ -65,21 +87,62 @@
</template>
<script>
import { getReportDetail } from '../../../api/performance/report';
export default {
name: "ReportDetail",
data() {
return {
activeName: 'first',
statisticsData: [], // 统计
errorList: []
errorCodeStatList: [],
top5InterfaceErrorStatList: [],
interfaceErrorDetailList: [],
detailData: null,
errorReport: null
}
},
mounted() {
this.getReportDetailData()
},
methods: {
// 导出excel
exportExcel() {
getReportDetailData() {
getReportDetail(this.$route.query.id).then(res => {
if (res.code === 200) {
this.detailData = res.data
this.statisticsData = JSON.parse(res.data.summaryReport)
this.errorReport = JSON.parse(res.data.errorReport)
this.errorCodeStatList = this.errorReport.errorCodeStatList
this.top5InterfaceErrorStatList = this.errorReport.top5InterfaceErrorStatList
this.interfaceErrorDetailList = this.errorReport.interfaceErrorDetailList
}
})
},
// 保留2为小数
getFormatToTwoDecimalPlaces(num) {
return Math.round(num * 100) / 100;
},
getLabel(index) {
const i = index + 1
return '#' + i + ' Error'
},
getErrorCount(index) {
const i = index + 1
return '#' + i + ' Error ' + 'Count'
},
getErrorName(val) {
var key1 = ''
for (let key in val.errorCodeCounts) {
key1 = key
}
return key1
},
getErrorCountDetail(val) {
var keyV = ''
for (let key in val.errorCodeCounts) {
keyV = val.errorCodeCounts[key]
}
return keyV
},
}
@@ -106,11 +169,6 @@ export default {
.tabs {
margin-top: 10px;
.btn-wrap {
display: flex;
justify-content: flex-end;
margin-bottom: 10px;
}
}
.title-error {