API Mock 服务器
从 OpenAPI 规范生成 mock API,配备有状态响应、故障注入和团队共享。
设计与 Schema
从 OpenAPI 规范定义 mock 路由,选择延迟和故障注入及存储模型。
为什么
Mock 服务器前置契约测试。前端和后端对着同一 OpenAPI mock 编码,集成从数周降到数天。故障注入(超时、500)在生产前捕获前端错误处理,生产修复成本高 10 倍。
何时使用
在前端和后端分歧之前。新 API 第一周从 OpenAPI 规范生成 mock。
如何做
1. 编写或获取 OpenAPI 规范
- 定义每个端点、请求/响应 schema 和状态码
- schema 中用 examples 提供真实 mock 数据
- 用 redocly 或 openapi-cli lint 校验
2. 生成 mock 服务器
- Prism:npx prism mock openapi.yaml -> 4010 端口即时 mock
- MSW(Mock Service Worker):Storybook/测试中浏览器内 mock
- WireMock:JVM 项目或独立 Docker
3. 用 Faker 加动态数据
- 列表用 Faker 生成值替换静态示例
- GET /users -> 25 个假用户含真实姓名/邮箱
- Faker 设种子以可复现测试
4. 设计故障注入
- 延迟:/slow 端点加 2 秒延迟测加载态
- 错误:/error 返回 500 测错误边界
- 经 header 配置:X-Mock-Status: 500
5. 规划存储模型
- 无状态(默认):每次请求返回新 Faker 数据
- 有状态:内存存储,POST 后 GET 返回创建项
- CRUD 演示和集成测试选有状态
示例
# openapi.yaml(节选)
paths:
/users:
get:
responses:
"200":
content:
application/json:
schema: { $ref: "#/components/schemas/UserList" }
example:
- { id: 1, email: "[email protected]", role: "admin" }
post:
requestBody:
content:
application/json:
schema: { $ref: "#/components/schemas/NewUser" }
responses:
"201":
content:
application/json:
schema: { $ref: "#/components/schemas/User" }
# 生成 mock
$ npx prism mock openapi.yaml
[CLI] ✔ Prism 正在监听 http://localhost:4010
# GET /users -> 200 返回示例数组
# POST /users -> 201 返回生成的用户
常见问题
- ⚠无 OpenAPI 规范——前端后端口头协议,一周内漂移,集成调试数天
- ⚠仅静态示例——GET /users 永远返回同样 3 用户,隐藏分页和空态 bug
- ⚠无故障注入——前端开发中从不见 500,上线无错误边界
最佳实践
- ✓从 OpenAPI 规范生成 mock,非手写——规范是 mock 和服务器的唯一真源
- ✓列表端点用 Faker,使分页、排序、空态可用真实数据量测试
- ✓加故障注入 header(X-Mock-Status: 500),QA 无需改代码即可触发错误路径
实现与动态响应
构建有状态 mock、Faker 动态数据和条件路由,用于真实演示和测试。
为什么
前端需测试创建后列表流程时静态 mock 失效。有状态 mock(内存存储)让 CRUD 序列在 Storybook 和测试中端到端工作,无需后端,在进 staging 前捕获集成 bug。
何时使用
静态 mock 服务正常路径后。为 CRUD 演示和集成测试加有状态路由。
如何做
1. 建内存存储
- const db = { users: [], posts: [] }——重启重置
- 启动时用 Faker 生成 25 条种子
- 需跨重启则持久化到 JSON 文件
2. CRUD 接入存储
- GET /users -> db.users(分页:?page&limit)
- POST /users -> 推入 db.users,返回 201 带新 id
- PUT /users/:id -> 查找替换,缺失 404
- DELETE /users/:id -> 过滤移除,204
3. 加动态数据生成
- Faker.person.fullName()、Faker.internet.email()
- 日期:Faker.date.recent({ days: 30 })
- 嵌套:帖子有作者(引用存储中的用户)
4. 经 header 条件路由
- X-Mock-Delay: 2000 -> 响应前 setTimeout 2 秒
- X-Mock-Status: 500 -> 返回 500 带错误体
- X-Mock-Empty: true -> 返回 [] 测空态
5. 集成前端测试套件
- Jest/Vitest 中 MSW:拦截 fetch,返回 mock 响应
- Storybook:MSW 插件让 story 命中 mock 非真实 API
- 测试文件间重置存储避免交叉污染
示例
// Express + Faker 有状态 mock
import express from "express";
import { faker } from "@faker-js/faker";
const app = express();
const db = { users: Array.from({ length: 25 }, (_, i) => ({
id: i + 1,
name: faker.person.fullName(),
email: faker.internet.email(),
role: faker.helpers.arrayElement(["admin", "member"]),
})) };
app.get("/users", (req, res) => {
const { page = 1, limit = 10 } = req.query;
const start = (page - 1) * limit;
res.json({ data: db.users.slice(start, start + +limit),
total: db.users.length });
});
app.post("/users", (req, res) => {
const user = { id: db.users.length + 1, ...req.body };
db.users.push(user);
res.status(201).json(user);
});
// 经 header 故障注入
app.use((req, res, next) => {
if (req.headers["x-mock-status"]) {
return res.status(+req.headers["x-mock-status"]).json({ error: "mock" });
}
next();
});
常见问题
- ⚠CRUD 演示用无状态 mock——POST 后 GET 返回旧数据,演示像坏了
- ⚠测试间不重置存储——测试 A 创建的用户泄漏到测试 B,导致不稳定失败
- ⚠Faker 不设种子——每次运行数据变化,快照测试随机失败
最佳实践
- ✓CRUD 端点用有状态内存存储,使创建后列表流程在演示和测试中可用
- ✓测试文件间重置 mock 存储(beforeEach)防止交叉数据污染
- ✓CI 中 Faker 设固定种子(faker.seed(123))使生成数据可复现
部署与共享
部署为 Docker 镜像、npm 包或托管服务,让全团队共享一个 mock。
为什么
Mock 服务器仅在全团队共用时有用。托管/共享 mock(Docker 镜像或提交到仓库的 MSW)确保前端、移动端、QA 对相同契约测试,消除'我机器上能跑'的集成失败。
何时使用
mock 正常路径稳定时。前端集成开始前打包,确保单一真源。
如何做
1. 打包为 Docker 镜像
- FROM node:20-alpine,复制 mock 服务器,CMD prism mock openapi.yaml
- 推到 ghcr.io 或 Docker Hub,带 semver 标签
- 团队运行:docker run -p 4010:4010 ghcr.io/org/api-mock:1.2.0
2. 或将 MSW handlers 提交到仓库
- src/mocks/handlers.ts——提交、版本控制
- 前端在开发模式和测试中导入 handlers
- OpenAPI 规范变更的同一 PR 更新 handlers
3. 部署共享托管实例
- Fly.io 或 Railway:部署 Prism 服务器,分享 URL
- QA 和移动端指向此 URL 做手动测试
- 用简单 API key header 保护避免公开暴露
4. 保持 mock 与规范同步
- CI 步骤:每个 PR 从 openapi.yaml 重新生成 mock
- 提交的 handlers 与规范偏离则构建失败
- 用 spectral lint 规范并阻断破坏性变更
5. 为团队写文档
- README:本地运行、注入故障、重置方法
- 加 /__reset 端点清除有状态存储,演示间用
- 列出可用 X-Mock-* header 及示例
示例
# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY openapi.yaml .
RUN npm install -g @stoplight/prism-cli
EXPOSE 4010
CMD ["prism", "mock", "openapi.yaml", "-h", "0.0.0.0"]
# CI:重新生成 mock + lint 规范(GitHub Actions)
- name: Lint OpenAPI
run: npx @redocly/cli lint openapi.yaml
- name: 检查 handlers 同步
run: npx openapi-typescript openapi.yaml -o src/api/types.ts
&& git diff --exit-code src/api/types.ts
# Fly.io 共享实例
$ fly deploy --image ghcr.io/org/api-mock:1.2.0
-> https://api-mock.fly.dev(分享给 QA + 移动团队)
常见问题
- ⚠每人跑不同 mock 版本——前端测试通过,集成失败因契约漂移
- ⚠无 CI 检查规范-handler 同步——handlers 过期,mock 返回真实 API 已无的字段
- ⚠共享 mock 公开暴露无认证——有人发现并探测你的 API 面
最佳实践
- ✓将 MSW handlers 提交到仓库,与 OpenAPI 规范同 PR 更新——单一真源
- ✓加 CI 步骤从规范重新生成类型并在 diff 时失败——自动捕获规范-handler 漂移
- ✓任何共享托管 mock 用 API key header 保护,无认证不部署到公开域名