274 lines
7.0 KiB
Markdown
274 lines
7.0 KiB
Markdown
---
|
||
name: 前端 API 测试基础设施
|
||
description: 为 Vue3+TypeScript 前端建立 Vitest 测试框架,Mock Axios 测试 11 个 API 模块
|
||
created: 2026-04-25
|
||
---
|
||
|
||
# 前端 API 测试基础设施设计文档
|
||
|
||
## 一、背景与目标
|
||
|
||
### 当前状态
|
||
|
||
- 前端基于 Vue 3 + TypeScript + Vite + Axios
|
||
- 11 个 API 模块 (`src/api/*.ts`) 封装 HTTP 请求
|
||
- **测试完全缺失**:无 vitest 配置,无 .spec/.test 文件
|
||
|
||
### 目标
|
||
|
||
建立前端 API 层测试基础设施,确保:
|
||
1. API 请求参数/响应类型正确
|
||
2. 错误处理逻辑覆盖
|
||
3. 前后端契约一致性验证
|
||
4. CI/CD 集成准备
|
||
|
||
## 二、技术选型
|
||
|
||
| 项目 | 选择 | 原因 |
|
||
|------|------|------|
|
||
| 测试框架 | Vitest | Vite 生态原生支持,配置简单,ESM 原生 |
|
||
| Mock 工具 | vi.mock (Vitest 内置) | 无额外依赖,Axios mock 简洁 |
|
||
| 断言库 | Vitest expect | 内置,支持 TypeScript 类型推断 |
|
||
| 覆盖率 | @vitest/coverage-v8 | Istanbul 覆盖率报告 |
|
||
|
||
## 三、架构设计
|
||
|
||
### 目录结构
|
||
|
||
```
|
||
frontend/
|
||
├── vitest.config.ts # Vitest 配置文件
|
||
├── src/api/
|
||
│ ├── __tests__/ # API 测试目录
|
||
│ │ ├── contract.spec.ts # 合同 API 测试
|
||
│ │ ├── zspContract.spec.ts # 中尚鹏合同
|
||
│ │ ├── zspFinances.spec.ts # 财务模块
|
||
│ │ ├── applyDelivery.spec.ts # 提货申请
|
||
│ │ ├── deliveryDetails.spec.ts
|
||
│ │ ├── inventory.spec.ts
|
||
│ │ ├── ownershipTransfer.spec.ts
|
||
│ │ ├── company.spec.ts
|
||
│ │ ├── user.spec.ts
|
||
│ │ ├── invoice.spec.ts
|
||
│ │ ├── settlement.spec.ts
|
||
│ │ └── reconciliation.spec.ts
|
||
│ └── test-utils.ts # Mock 工具函数
|
||
└── package.json # 新增 vitest 相关依赖
|
||
```
|
||
|
||
### Vitest 配置
|
||
|
||
```typescript
|
||
// vitest.config.ts
|
||
import { defineConfig } from 'vitest/config'
|
||
import vue from '@vitejs/plugin-vue'
|
||
import path from 'path'
|
||
|
||
export default defineConfig({
|
||
plugins: [vue()],
|
||
test: {
|
||
environment: 'jsdom',
|
||
include: ['src/api/__tests__/**/*.spec.ts'],
|
||
coverage: {
|
||
provider: 'v8',
|
||
reporter: ['text', 'html'],
|
||
include: ['src/api/**/*.ts'],
|
||
exclude: ['src/api/__tests__/**', 'src/api/test-utils.ts']
|
||
}
|
||
},
|
||
resolve: {
|
||
alias: {
|
||
'@': path.resolve(__dirname, './src')
|
||
}
|
||
}
|
||
})
|
||
```
|
||
|
||
### Mock 工具函数
|
||
|
||
```typescript
|
||
// src/api/test-utils.ts
|
||
import { vi } from 'vitest'
|
||
import { http } from '@/utils/http'
|
||
|
||
/**
|
||
* Mock http.request 方法
|
||
* @param response 模拟响应数据
|
||
* @param status HTTP 状态码 (默认 200)
|
||
*/
|
||
export function mockHttpRequest(response: any, status = 200) {
|
||
vi.spyOn(http, 'request').mockResolvedValue({
|
||
success: true,
|
||
data: response,
|
||
status
|
||
})
|
||
}
|
||
|
||
/**
|
||
* Mock HTTP 错误响应
|
||
* @param message 错误信息
|
||
* @param status HTTP 状态码
|
||
*/
|
||
export function mockHttpError(message: string, status: number) {
|
||
vi.spyOn(http, 'request').mockRejectedValue({
|
||
success: false,
|
||
message,
|
||
status
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 清除所有 mock
|
||
*/
|
||
export function clearMocks() {
|
||
vi.restoreAllMocks()
|
||
}
|
||
```
|
||
|
||
## 四、测试模式
|
||
|
||
每个 API 模块测试遵循统一模式:
|
||
|
||
### 4.1 请求成功测试
|
||
|
||
```typescript
|
||
describe('getContractList', () => {
|
||
it('应返回合同列表', async () => {
|
||
const mockData = [{ id: 1, name: '合同1' }]
|
||
mockHttpRequest(mockData)
|
||
|
||
const result = await getContractList()
|
||
expect(result.success).toBe(true)
|
||
expect(result.data).toEqual(mockData)
|
||
})
|
||
})
|
||
```
|
||
|
||
### 4.2 参数验证测试
|
||
|
||
```typescript
|
||
it('带 contractType 参数应正确传递', async () => {
|
||
mockHttpRequest([])
|
||
|
||
await getContractList({ contractType: 'business' })
|
||
expect(http.request).toHaveBeenCalledWith('get', '/api/contract', {
|
||
params: { contractType: 'business' }
|
||
})
|
||
})
|
||
```
|
||
|
||
### 4.3 错误处理测试
|
||
|
||
```typescript
|
||
describe('错误处理', () => {
|
||
it('401 未授权应抛出错误', async () => {
|
||
mockHttpError('Unauthorized', 401)
|
||
|
||
await expect(getContractList()).rejects.toMatchObject({
|
||
status: 401
|
||
})
|
||
})
|
||
|
||
it('500 服务器错误应抛出错误', async () => {
|
||
mockHttpError('Internal Server Error', 500)
|
||
|
||
await expect(getContractList()).rejects.toMatchObject({
|
||
status: 500
|
||
})
|
||
})
|
||
})
|
||
```
|
||
|
||
### 4.4 类型验证
|
||
|
||
```typescript
|
||
it('响应应符合 BusinessContract 类型', async () => {
|
||
const mockData: BusinessContract[] = [
|
||
{
|
||
id: 1,
|
||
name: '测试合同',
|
||
contractType: 'business',
|
||
contractCode: 'C001',
|
||
// ... 其他必填字段
|
||
}
|
||
]
|
||
mockHttpRequest(mockData)
|
||
|
||
const result = await getContractList()
|
||
// TypeScript 编译时类型检查,运行时验证结构
|
||
expect(result.data[0]).toHaveProperty('id')
|
||
expect(result.data[0]).toHaveProperty('name')
|
||
})
|
||
```
|
||
|
||
## 五、API 模块测试清单
|
||
|
||
| 模块 | 文件 | 主要 API 函数 | 预估测试数 |
|
||
|------|------|---------------|------------|
|
||
| Contract | `contract.spec.ts` | getContractList, getContract, submitContract, batchUploadContracts, updateContract, deleteContract + folders/batches | 15-20 |
|
||
| ZSP Contract | `zspContract.spec.ts` | getFolders, getContracts, uploadContract, createDetail, importExcel, exportExcel | 10-15 |
|
||
| ZSP Finances | `zspFinances.spec.ts` | CRUD for freight/misc/service/cost/profit | 12-15 |
|
||
| Apply Delivery | `applyDelivery.spec.ts` | list, get, create, update, cancel, delete | 10 |
|
||
| Delivery Details | `deliveryDetails.spec.ts` | list, get, create, update, delete | 8 |
|
||
| Inventory | `inventory.spec.ts` | warehouses, inbound, summary | 8 |
|
||
| Ownership Transfer | `ownershipTransfer.spec.ts` | list, get, create, createWithFiles, delete | 8 |
|
||
| Company | `company.spec.ts` | folders, files CRUD | 8 |
|
||
| User | `user.spec.ts` | login, register, getUser, refreshToken | 8 |
|
||
| Invoice | `invoice.spec.ts` | 上游/下游/货代发票 CRUD | 10 |
|
||
| Settlement | `settlement.spec.ts` | CRUD | 6 |
|
||
| Reconciliation | `reconciliation.spec.ts` | 对账单 CRUD | 6 |
|
||
|
||
**总计预估:90-110 个测试用例**
|
||
|
||
## 六、执行命令
|
||
|
||
```bash
|
||
# 安装依赖
|
||
pnpm add -D vitest @vitest/coverage-v8 jsdom
|
||
|
||
# 运行测试
|
||
pnpm test
|
||
|
||
# 运行覆盖率
|
||
pnpm test:coverage
|
||
|
||
# 监听模式
|
||
pnpm test:watch
|
||
```
|
||
|
||
### package.json 新增脚本
|
||
|
||
```json
|
||
{
|
||
"scripts": {
|
||
"test": "vitest run",
|
||
"test:watch": "vitest",
|
||
"test:coverage": "vitest run --coverage"
|
||
}
|
||
}
|
||
```
|
||
|
||
## 七、CI/CD 集成预留
|
||
|
||
后续可添加 GitHub Actions 或其他 CI 配置:
|
||
|
||
```yaml
|
||
# .github/workflows/test.yml (预留)
|
||
- name: Run frontend tests
|
||
run: cd frontend && pnpm test:coverage
|
||
```
|
||
|
||
## 八、验收标准
|
||
|
||
1. 所有 11 个 API 模块有对应 `.spec.ts` 文件
|
||
2. 测试覆盖率 > 80%(API 模块代码行覆盖)
|
||
3. 所有测试通过,无 TypeScript 编译错误
|
||
4. `pnpm test` 命令可用
|
||
5. 覆盖率报告生成 (`coverage/index.html`)
|
||
|
||
## 九、后续扩展方向
|
||
|
||
- 组件测试 (`src/components/`)
|
||
- 页面集成测试 (`src/views/`)
|
||
- E2E 测试 (Playwright)
|
||
- 真实后端集成测试层 |