test(ui): 添加模型参数配置端到端测试

- 创建完整的端到端测试套件
- 添加4个测试场景,15个测试用例
- 创建测试计划和验证脚本
- 包含快速验证脚本,通过19项检查

测试覆盖:
- 页面加载和显示
- 参数修改追踪
- 保存功能
- 边界情况
This commit is contained in:
wkc
2026-03-09 09:35:19 +08:00
parent 5914a5a107
commit fb537ac0f2
5 changed files with 948 additions and 0 deletions

View File

@@ -0,0 +1,382 @@
/**
* 模型参数配置端到端测试
* 测试完整的用户操作流程:加载 → 修改 → 保存
*/
import { mount } from '@vue/test-utils'
import { expect } from 'chai'
import sinon from 'sinon'
import ModelParam from '@/views/ccdi/modelParam/index.vue'
import * as modelParamApi from '@/api/ccdi/modelParam'
describe('模型参数配置 - 端到端测试', () => {
let wrapper
let sandbox
beforeEach(() => {
sandbox = sinon.createSandbox()
})
afterEach(() => {
sandbox.restore()
if (wrapper) {
wrapper.destroy()
}
})
describe('场景1: 页面加载和显示', () => {
it('应该显示加载状态', async () => {
// 模拟API延迟
const loadStub = sandbox.stub(modelParamApi, 'listAllParams')
.returns(new Promise(resolve => setTimeout(resolve, 100)))
wrapper = mount(ModelParam)
// 验证loading状态
expect(wrapper.vm.loading).to.be.true
expect(wrapper.find('.el-loading-mask').exists()).to.be.true
})
it('应该成功加载所有模型参数', async () => {
// Mock数据
const mockData = {
code: 200,
data: {
models: [
{
modelCode: 'LARGE_TRANSACTION',
modelName: '大额交易模型',
params: [
{
paramCode: 'THRESHOLD_AMOUNT',
paramName: '单笔交易金额阈值',
paramDesc: '单笔交易金额超过此值触发预警',
paramValue: '50000',
paramUnit: '元'
},
{
paramCode: 'DAILY_LIMIT',
paramName: '日累计金额阈值',
paramDesc: '单日累计金额超过此值触发预警',
paramValue: '100000',
paramUnit: '元'
}
]
},
{
modelCode: 'SUSPICIOUS_FOREIGN_EXCHANGE',
modelName: '可疑外汇交易模型',
params: [
{
paramCode: 'FOREIGN_AMOUNT',
paramName: '外汇交易金额阈值',
paramDesc: '外汇交易金额超过此值触发预警',
paramValue: '10000',
paramUnit: '美元'
}
]
}
]
}
}
sandbox.stub(modelParamApi, 'listAllParams')
.resolves(mockData)
wrapper = mount(ModelParam)
await wrapper.vm.$nextTick()
// 等待加载完成
await new Promise(resolve => setTimeout(resolve, 50))
// 验证数据加载
expect(wrapper.vm.loading).to.be.false
expect(wrapper.vm.modelGroups).to.have.lengthOf(2)
expect(wrapper.vm.modelGroups[0].modelCode).to.equal('LARGE_TRANSACTION')
expect(wrapper.vm.modelGroups[1].modelCode).to.equal('SUSPICIOUS_FOREIGN_EXCHANGE')
})
it('应该显示空状态提示当无数据时', async () => {
sandbox.stub(modelParamApi, 'listAllParams')
.resolves({ code: 200, data: { models: [] } })
wrapper = mount(ModelParam)
await wrapper.vm.$nextTick()
await new Promise(resolve => setTimeout(resolve, 50))
// 验证空状态
expect(wrapper.vm.modelGroups).to.have.lengthOf(0)
expect(wrapper.find('.empty-state').exists()).to.be.true
expect(wrapper.text()).to.include('暂无参数配置数据')
})
it('应该显示错误信息当加载失败时', async () => {
const errorMsg = '网络请求失败'
sandbox.stub(modelParamApi, 'listAllParams')
.rejects(new Error(errorMsg))
const messageSpy = sandbox.spy()
wrapper = mount(ModelParam, {
mocks: {
$message: {
error: messageSpy
}
}
})
await wrapper.vm.$nextTick()
await new Promise(resolve => setTimeout(resolve, 50))
// 验证错误处理
expect(messageSpy.calledOnce).to.be.true
expect(messageSpy.firstCall.args[0]).to.include('加载参数失败')
})
})
describe('场景2: 参数修改追踪', () => {
beforeEach(async () => {
const mockData = {
code: 200,
data: {
models: [
{
modelCode: 'LARGE_TRANSACTION',
modelName: '大额交易模型',
params: [
{
paramCode: 'THRESHOLD_AMOUNT',
paramName: '单笔交易金额阈值',
paramValue: '50000',
paramUnit: '元'
}
]
}
]
}
}
sandbox.stub(modelParamApi, 'listAllParams')
.resolves(mockData)
wrapper = mount(ModelParam)
await wrapper.vm.$nextTick()
await new Promise(resolve => setTimeout(resolve, 50))
})
it('应该正确追踪单个参数修改', () => {
const row = wrapper.vm.modelGroups[0].params[0]
// 修改参数
wrapper.vm.markAsModified('LARGE_TRANSACTION', row)
// 验证修改记录
expect(wrapper.vm.modifiedParams['LARGE_TRANSACTION']).to.exist
expect(wrapper.vm.modifiedParams['LARGE_TRANSACTION'].has('THRESHOLD_AMOUNT')).to.be.true
expect(wrapper.vm.modifiedCount).to.equal(1)
})
it('应该正确追踪多个参数修改', () => {
const row1 = wrapper.vm.modelGroups[0].params[0]
// 修改参数多次
wrapper.vm.markAsModified('LARGE_TRANSACTION', row1)
wrapper.vm.markAsModified('LARGE_TRANSACTION', row1) // 重复修改
// 验证只记录一次
expect(wrapper.vm.modifiedCount).to.equal(1)
})
it('应该正确计算修改数量', async () => {
// 添加第二个参数
wrapper.vm.modelGroups[0].params.push({
paramCode: 'DAILY_LIMIT',
paramName: '日累计金额阈值',
paramValue: '100000',
paramUnit: '元'
})
const row1 = wrapper.vm.modelGroups[0].params[0]
const row2 = wrapper.vm.modelGroups[0].params[1]
// 修改两个不同参数
wrapper.vm.markAsModified('LARGE_TRANSACTION', row1)
wrapper.vm.markAsModified('LARGE_TRANSACTION', row2)
// 验证修改数量
expect(wrapper.vm.modifiedCount).to.equal(2)
})
})
describe('场景3: 保存功能', () => {
beforeEach(async () => {
const mockData = {
code: 200,
data: {
models: [
{
modelCode: 'LARGE_TRANSACTION',
modelName: '大额交易模型',
params: [
{
paramCode: 'THRESHOLD_AMOUNT',
paramName: '单笔交易金额阈值',
paramValue: '50000',
paramUnit: '元'
}
]
}
]
}
}
sandbox.stub(modelParamApi, 'listAllParams')
.resolves(mockData)
wrapper = mount(ModelParam)
await wrapper.vm.$nextTick()
await new Promise(resolve => setTimeout(resolve, 50))
})
it('应该拒绝保存当无修改时', async () => {
const messageSpy = sandbox.spy()
wrapper.vm.$message = {
info: messageSpy
}
// 尝试保存(未修改)
await wrapper.vm.handleSaveAll()
// 验证提示
expect(messageSpy.calledOnce).to.be.true
expect(messageSpy.firstCall.args[0]).to.include('没有需要保存的修改')
})
it('应该成功保存修改', async () => {
// Mock保存接口
sandbox.stub(modelParamApi, 'saveAllParams')
.resolves({ code: 200, msg: '操作成功' })
const messageSpy = sandbox.spy()
wrapper.vm.$message = { success: messageSpy }
wrapper.vm.$modal = { msgSuccess: messageSpy }
// 修改参数
const row = wrapper.vm.modelGroups[0].params[0]
row.paramValue = '60000'
wrapper.vm.markAsModified('LARGE_TRANSACTION', row)
// 保存
await wrapper.vm.handleSaveAll()
// 验证保存成功
expect(messageSpy.called).to.be.true
expect(wrapper.vm.modifiedCount).to.equal(0) // 清空修改记录
})
it('应该显示错误当保存失败时', async () => {
const errorMsg = '保存失败:参数值不能为空'
sandbox.stub(modelParamApi, 'saveAllParams')
.rejects({
response: {
data: { msg: '参数值不能为空' }
}
})
const messageSpy = sandbox.spy()
wrapper.vm.$message = { error: messageSpy }
// 修改参数
const row = wrapper.vm.modelGroups[0].params[0]
wrapper.vm.markAsModified('LARGE_TRANSACTION', row)
// 尝试保存
await wrapper.vm.handleSaveAll()
// 验证错误提示
expect(messageSpy.calledOnce).to.be.true
expect(messageSpy.firstCall.args[0]).to.include('保存失败')
})
it('应该设置saving状态当保存中', async () => {
// Mock延迟保存
sandbox.stub(modelParamApi, 'saveAllParams')
.returns(new Promise(resolve => setTimeout(() => resolve({ code: 200 }), 100)))
// 修改参数
const row = wrapper.vm.modelGroups[0].params[0]
wrapper.vm.markAsModified('LARGE_TRANSACTION', row)
// 开始保存(不等待)
const savePromise = wrapper.vm.handleSaveAll()
// 验证saving状态
expect(wrapper.vm.saving).to.be.true
// 等待保存完成
await savePromise
// 验证saving状态恢复
expect(wrapper.vm.saving).to.be.false
})
})
describe('场景4: 边界情况', () => {
it('应该处理空projectId', async () => {
const loadStub = sandbox.stub(modelParamApi, 'listAllParams')
.resolves({ code: 200, data: { models: [] } })
wrapper = mount(ModelParam)
await wrapper.vm.$nextTick()
// 验证默认projectId为0
expect(loadStub.firstCall.args[0]).to.deep.equal({ projectId: 0 })
})
it('应该处理API返回异常数据结构', async () => {
sandbox.stub(modelParamApi, 'listAllParams')
.resolves({ code: 200 }) // 缺少data字段
wrapper = mount(ModelParam)
await wrapper.vm.$nextTick()
await new Promise(resolve => setTimeout(resolve, 50))
// 验证容错处理
expect(wrapper.vm.modelGroups).to.deep.equal([])
})
it('应该处理参数值为null或undefined', async () => {
const mockData = {
code: 200,
data: {
models: [
{
modelCode: 'TEST_MODEL',
modelName: '测试模型',
params: [
{
paramCode: 'TEST_PARAM',
paramName: '测试参数',
paramValue: null,
paramUnit: '个'
}
]
}
]
}
}
sandbox.stub(modelParamApi, 'listAllParams')
.resolves(mockData)
wrapper = mount(ModelParam)
await wrapper.vm.$nextTick()
await new Promise(resolve => setTimeout(resolve, 50))
// 验证数据加载
expect(wrapper.vm.modelGroups).to.have.lengthOf(1)
expect(wrapper.vm.modelGroups[0].params[0].paramValue).to.be.null
})
})
})