Files
zsp-project/docs/superpowers/specs/2026-04-25-frontend-api-test-infra-design.md
2026-06-03 20:59:39 +08:00

7.0 KiB
Raw Blame History

name, description, created
name description created
前端 API 测试基础设施 为 Vue3+TypeScript 前端建立 Vitest 测试框架Mock Axios 测试 11 个 API 模块 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 配置

// 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 工具函数

// 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 请求成功测试

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 参数验证测试

it('带 contractType 参数应正确传递', async () => {
  mockHttpRequest([])

  await getContractList({ contractType: 'business' })
  expect(http.request).toHaveBeenCalledWith('get', '/api/contract', {
    params: { contractType: 'business' }
  })
})

4.3 错误处理测试

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 类型验证

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 个测试用例

六、执行命令

# 安装依赖
pnpm add -D vitest @vitest/coverage-v8 jsdom

# 运行测试
pnpm test

# 运行覆盖率
pnpm test:coverage

# 监听模式
pnpm test:watch

package.json 新增脚本

{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage"
  }
}

七、CI/CD 集成预留

后续可添加 GitHub Actions 或其他 CI 配置:

# .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)
  • 真实后端集成测试层