Mock数据使用指南

Mock数据使用指南

本项目Mock数据管理的完整规范,涵盖前端开发、单元测试和集成测试中的Mock策略


📋 指南使用说明

核心目的:本指南为前端开发提供Mock数据使用的统一标准,确保在开发和测试阶段的数据一致性和可维护性。

适用范围

  • 后端API尚未就绪时的前端开发
  • 单元测试中的API调用模拟
  • 集成测试中的数据模拟
  • Storybook组件展示数据

Mock策略分类

  1. 开发Mock:通过useMockData() composable提供数据
  2. 测试Mock:使用MSW或直接mock API调用
  3. Mock API服务器:通过apiMock/目录提供静态JSON响应

🏗️ Mock数据架构概览

环境控制

通过环境变量 NUXT_MOCK_API_PROXY 控制Mock模式:

NUXT_MOCK_API_PROXY=1   # 使用Mock API服务器(./apiMock目录)
NUXT_MOCK_API_PROXY=0   # 连接真实后端API

nuxt.config.ts配置

export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      mockApiProxy: process.env.NUXT_MOCK_API_PROXY,
    },
  },
})

Mock数据源层级

frontend/
├── src/
│   ├── composables/
│   │   └── useMockData.ts          # 主要Mock数据composable
├── apiMock/                        # Mock API服务器目录
│   └── api/
│       ├── auth/
│       │   └── login.json          # 登录接口Mock数据
│       └── users/
│           └── index.json          # 用户列表Mock数据
└── tests/
    └── setup/
        └── msw.ts                  # MSW处理器配置

🛠️ 开发阶段Mock数据

1. useMockData() Composable

文件位置frontend/src/composables/useMockData.ts

核心功能

// 获取用户列表(模拟)
fetchUsers(): Promise<User[]>

// 获取单个用户
fetchUser(id: number): Promise<User>

// 模拟保存操作
saveData<T>(data: T): Promise<T>

// 模拟删除操作
deleteData(id: number): Promise<boolean>

// 模拟搜索
searchData<T>(keyword: string): Promise<T[]>

// 模拟分页数据
fetchPaginatedData<T>(page: number, pageSize: number): Promise<PaginatedResponse<T>>

设计原则

  • 模拟网络延迟(默认500ms)
  • 返回类型与真实API保持一致
  • 支持错误场景模拟
  • 便于后期平滑替换为真实API

2. 页面/组件中使用示例

<script setup lang="ts">
import { useMockData } from '~/composables/useMockData'

const { fetchUsers, saveData } = useMockData()

const users = ref<User[]>([])
const loading = ref(false)

onMounted(async () => {
  loading.value = true
  users.value = await fetchUsers()  // 使用Mock数据
  loading.value = false
})

const handleSave = async (data: UserForm) => {
  const result = await saveData(data)  // 模拟保存
  console.log('Mock save result:', result)
}
</script>

3. Mock API服务器

工作原理

  • 开发模式下,Nuxt扫描apiMock目录
  • .json文件作为API端点响应
  • 支持RESTful路由模式

目录结构示例

apiMock/
├── api/
│   ├── auth/
│   │   └── login.json          # POST /api/auth/login
│   ├── users/
│   │   ├── index.json          # GET /api/users
│   │   └── [id].json           # GET /api/users/:id
│   └── agency-groups/
│       └── index.json          # GET /api/agency-groups

JSON文件格式

{
  "messageId": "mock-001",
  "data": {
    "agencyGroups": [
      {
        "agencyGroupId": 1,
        "agencyGroupName": "テストグループA",
        "groupDescription": "説明文"
      }
    ]
  },
  "error": null
}

🧪 测试阶段Mock数据

1. 前端单元测试(Vitest)

方案A:使用MSW(推荐用于集成测试)

// tests/setup/msw.ts
import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'

export const handlers = [
  http.get('/api/users/:id', ({ params }) => {
    const { id } = params
    return HttpResponse.json({
      messageId: 'test-001',
      data: {
        id,
        name: 'Mock User',
        email: 'mock@example.com'
      },
      error: null
    })
  }),
]

export const server = setupServer(...handlers)

方案B:直接mock ofetch(用于单元测试)

// userApi.spec.ts
import { describe, test, expect, vi, beforeEach } from 'vitest'
import { fetchUser } from './userApi'

vi.mock('ofetch', () => ({
  default: vi.fn(),
}))

describe('userApi', () => {
  test('should fetch user data successfully', async () => {
    // Arrange
    const mockUser = { 
      messageId: 'test-001',
      data: { id: 1, name: 'Alice', email: 'alice@example.com' },
      error: null
    }
    const { default: ofetch } = await import('ofetch')
    ;(ofetch as any).mockResolvedValue(mockUser)

    // Act & Assert
    const result = await fetchUser(1)
    expect(result).toEqual(mockUser)
  })
})

2. 后端单元测试(Java + Spring Boot)

Controller测试(@WebMvcTest)

@WebMvcTest(UserController.class)
class UserControllerTest {
    @MockBean
    private UserService userService;

    @Autowired
    private MockMvc mockMvc;

    @Test
    void shouldReturnUserWhenUserExists() throws Exception {
        // Arrange
        Long userId = 1L;
        UserResponse mockUser = new UserResponse(userId, "Alice", "alice@example.com");
        when(userService.getUserById(userId)).thenReturn(mockUser);

        // Act & Assert
        mockMvc.perform(get("/api/users/{id}", userId))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.id").value(1));
    }
}

Service测试(Mockito)

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    void shouldGetUserById() {
        // Arrange
        Long userId = 1L;
        User mockUser = new User(userId, "Alice", "alice@example.com");
        when(userRepository.findById(userId)).thenReturn(Optional.of(mockUser));

        // Act & Assert
        UserResponse result = userService.getUserById(userId);
        assertThat(result).isNotNull();
    }
}

📋 Mock数据规范

1. 数据格式规范

标准消息结构(与真实API保持一致):

interface ApiResponse<T> {
  messageId: string      // 消息ID(用于追踪)
  data: T | null         // 响应数据
  error: {
    code: string         // 错误代码
    message: string      // 错误消息
    details?: any        // 错误详情
  } | null
}

2. Mock数据文件命名

场景命名规则示例
API响应数据api/资源名/操作.jsonapi/users/index.json
列表数据资源名-list.jsonusers-list.json
单条数据资源名-detail.jsonuser-detail.json
错误响应资源名-error.jsonlogin-error.json

3. Mock函数命名

操作类型命名前缀示例
获取列表fetchXxxfetchUsers()
获取单个fetchXxxByIdfetchUserById(1)
保存数据saveXxxsaveUser(data)
删除数据deleteXxxdeleteUser(1)
搜索数据searchXxxsearchUsers(keyword)
分页数据fetchXxxPaginatedfetchUsersPaginated(1, 10)

✅ Mock数据最佳实践

推荐做法

  1. 统一入口:通过useMockData()统一管理Mock函数
  2. 延迟模拟:使用delay()模拟网络延迟(默认500ms)
  3. 数据真实:Mock数据应贴近真实业务场景
  4. 类型安全:Mock函数返回类型与真实API一致
  5. 环境分离:使用环境变量控制Mock开关
  6. 错误场景:模拟网络错误、业务错误等异常情况

禁止事项

  • ❌ 生产环境使用Mock数据
  • ❌ 在组件中硬编码Mock数据逻辑
  • ❌ 测试中发起真实API调用
  • ❌ Mock函数包含复杂业务逻辑
  • ❌ 使用any类型,保持类型安全

🔄 Mock到真实API的迁移

迁移策略

阶段一:所有调用通过 useMockData() 进行

阶段二:创建真实API composable,保持相同接口

阶段三:替换导入路径,移除 useMockData()

迁移示例

// 迁移前:使用Mock
import { useMockData } from '~/composables/useMockData'
const { fetchUsers } = useMockData()

// 迁移后:使用真实API
import { useUsersApi } from '~/composables/api/useUsersApi'
const { fetchUsers } = useUsersApi()

📁 文件结构规范

frontend/
├── src/
│   ├── composables/
│   │   ├── useMockData.ts          # 主要Mock composable
│   │   └── api/                    # 真实API composables(后期替换)
│   │       └── useUsersApi.ts
│   └── types/
│       └── api/                    # API类型定义
│           └── user.ts
├── apiMock/                        # Mock API服务器
│   ├── api/
│   │   ├── auth/
│   │   │   └── login.json
│   │   └── users/
│   │       └── index.json
│   └── README.md
└── tests/
    ├── setup/
    │   ├── vitest.setup.ts        # 测试全局设置
    │   └── msw.ts                 # MSW处理器
    └── unit/
        └── composables/
            └── useMockData.spec.ts

🧪 测试覆盖率要求

测试类型覆盖率目标Mock策略
单元测试行覆盖率≥80%Mock API调用
集成测试核心流程覆盖MSW模拟
E2E测试关键用户路径Playwright网络拦截

🔗 相关文档参考

核心文档

  1. 前端实现标准docs/guidelines/implementation/frontend.md
  2. 前端单元测试指南docs/guidelines/testing/frontend-unittest-guidelines.md
  3. 后端单元测试指南docs/guidelines/testing/backend-unittest-guidelines.md

实现教程

  1. Mock实现指南frontend/docs/guides/mock-implementation.md
  2. 环境搭建指南frontend/docs/getting-started/installation.md

代码示例

  1. useMockData实现frontend/src/composables/useMockData.ts
  2. Mock API示例frontend/apiMock/api/目录

🎯 快速参考速查卡

Mock数据创建流程

1. 确定API响应数据结构
2. 创建类型定义(types/api/xxx.ts)
3. 在useMockData.ts中添加Mock函数
4. 可选:在apiMock/中添加静态JSON
5. 在组件中使用Mock函数
6. 后期替换为真实API

常用Mock函数

// 获取数据
const users = await fetchUsers()

// 保存数据
const result = await saveData(userData)

// 搜索数据
const results = await searchData(keyword)

// 分页数据
const pageData = await fetchPaginatedData(1, 10)

环境切换

# 使用Mock数据
cross-env NUXT_MOCK_API_PROXY=1 npm run dev

# 使用真实API
cross-env NUXT_MOCK_API_PROXY=0 npm run dev

核心原则:Mock数据是开发效率工具,不是业务逻辑替代品。保持Mock数据与真实API的一致性,确保平滑迁移。

最后更新: 2026-02-04
适用项目: APJ-B2B前端项目
相关标准: Nuxt 3、TypeScript严格模式、MSW、Vitest