前面两篇文章简单介绍了如何只用 Nextjs 在本地模拟 SSE 实现打字效果和通过 Streamdown 优雅地解析输出 AI 返回的 Markdown 文本,接下来就是如何测试了。
Mock 框架
Mock 框架有很多,个人倾向使用 Mock Service Worker (MSW) 来测 AI 相关的功能,在下面的详细对比中也有意将 MSW 放在第一位。
- MSW (Mock Service Worker)
- JSON Server
- WireMock
- Nock
- MirageJS
框架对比
1. 架构设计与工作原理
| 框架 | 工作原理 | 优点 | 缺点 |
|---|---|---|---|
| MSW | 基于 Service Worker API 拦截网络请求 | • 在网络层面拦截,透明度高 • 支持浏览器和 Node.js 环境 • 不侵入业务代码 | • 需要理解 Service Worker 概念 • 在某些环境下配置相对复杂 |
| JSON Server | 启动独立的 REST API 服务器 | • 零配置快速启动 • 自动生成 CRUD 接口 • 支持关系数据和查询 | • 仅限于 REST API • 功能相对简单 • 需要独立端口 |
| WireMock | 基于 HTTP 服务器的 Mock 框架 | • 功能强大,支持复杂场景 • 丰富的匹配规则 • 支持状态管理和故障注入 | • 主要面向 Java 生态 • 配置复杂 • 资源消耗较大 |
| Nock | HTTP 请求拦截库 | • 专门针对 Node.js 环境 • API 简洁直观 • 测试友好 | • 仅支持 Node.js • 不支持浏览器环境 • 功能相对单一 |
| MirageJS | 客户端服务器模拟框架 | • 提供完整的数据层模拟 • 支持数据库关系 • 内置 ORM | • 学习曲线陡峭 • 配置复杂 • 体积较大 |
2. 开发体验与易用性
| 框架 | 学习成本 | 配置复杂度 | API 设计 | TypeScript 支持 | 热重载 | 调试体验 |
|---|---|---|---|---|---|---|
| MSW | 中等 | 中等 | 声明式,直观 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| JSON Server | 极低 | 极简 | 约定优于配置 | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| WireMock | 高 | 复杂 | 功能丰富但冗长 | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| Nock | 低 | 简单 | 链式调用,简洁 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| MirageJS | 高 | 复杂 | ORM 风格 | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
详细说明:
- MSW: 声明式 API 设计优秀,TypeScript 支持完善,但 Service Worker 调试相对复杂
- JSON Server: 学习成本最低,一行命令启动,但功能相对基础
- WireMock: 企业级功能完善,但配置复杂,主要面向 Java 开发者
- Nock: API 简洁明了,测试友好,但仅限 Node.js 环境
- MirageJS: 功能最全面,但学习曲线陡峭,配置最复杂
3. 性能表现
| 框架 | 启动速度 | 内存占用 | 响应延迟 | 并发处理 |
|---|---|---|---|---|
| MSW | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| JSON Server | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| WireMock | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Nock | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| MirageJS | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
4. 功能覆盖度
| 功能特性 | MSW | JSON Server | WireMock | Nock | MirageJS |
|---|---|---|---|---|---|
| REST API | ✅ | ✅ | ✅ | ✅ | ✅ |
| GraphQL | ✅ | ❌ | ❌ | ❌ | ✅ |
| WebSocket/SSE | ✅ | ❌ | ❌ | ❌ | ❌ |
| 浏览器环境 | ✅ | ❌ | ❌ | ❌ | ✅ |
| Node.js 环境 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 请求录制回放 | ❌ | ❌ | ✅ | ❌ | ❌ |
| 故障注入 | ✅ | ❌ | ✅ | ✅ | ✅ |
| 延迟模拟 | ✅ | ❌ | ✅ | ❌ | ✅ |
| 状态管理 | 基础 | ❌ | ✅ | ❌ | ✅ |
| 数据持久化 | ❌ | ✅ | ✅ | ❌ | ❌ |
| 数据关系建模 | ❌ | 基础 | ❌ | ❌ | ✅ |
| 自定义中间件 | ✅ | ✅ | ✅ | ❌ | ✅ |
| 代理模式 | ❌ | ❌ | ✅ | ❌ | ❌ |
| TypeScript 支持 | ✅ | 部分 | 部分 | ✅ | ✅ |
| 热重载 | ✅ | ✅ | ✅ | ✅ | ✅ |
符号说明:
- ✅:完全支持
- 部分:部分支持
- 基础:基础功能
- ❌:不支持
5. 生态系统与社区支持
| 框架 | GitHub Stars | NPM 周下载量 | 社区活跃度 | 文档质量 | 维护状态 |
|---|---|---|---|---|---|
| MSW | 15.8k+ | 1.2M+ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 积极维护 |
| JSON Server | 72k+ | 800k+ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 稳定维护 |
| WireMock | 6.2k+ | N/A | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 积极维护 |
| Nock | 12.6k+ | 1.8M+ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 稳定维护 |
| MirageJS | 5.4k+ | 50k+ | ⭐⭐⭐ | ⭐⭐⭐⭐ | 维护缓慢 |
适合场景
🚀 适合 MSW 的场景:
- 现代前端应用开发(React、Vue、Angular)
- 同时支持开发和测试环境
- 模拟实时数据流(SSE、WebSocket)
- 统一的 Mock 解决方案
⚡ 适合 JSON Server 的场景:
- 快速原型验证
- 简单的 REST API Mock
- 最小学习成本
- 独立的 Mock 服务
🏢 适合 WireMock 的场景:
- 企业级功能(录制回放、故障注入)
- 微服务架构测试
- 复杂的网络场景模拟
- Java 技术栈集成
🧪 适合 Nock 的场景:
- Node.js 后端测试
- 精确的请求拦截验证
- 轻量级测试解决方案
- 与测试框架深度集成
🎯 适合 MirageJS 的场景:
- 完整的数据层模拟
- 复杂的数据关系建模
- 长期前端项目开发
- 离线开发能力
选择 MSW 的优势
- 架构先进: 基于 Service Worker 设计,在网络层面拦截请求,不侵入业务代码
- 跨环境支持: 同时支持浏览器和 Node.js 环境,一套代码多环境复用
- 开发体验: 优秀的 TypeScript 支持和现代化的 API 设计
- 功能完善: 支持 REST、GraphQL、实时数据流等多种协议
- 社区活跃: 持续的更新和丰富的生态系统
安装 MSW
演示基于 Nextjs
- 安装 MSW
// npm
npm i msw --save-dev
// yarn
yarn add msw -D
- 额外的一步:生成浏览器端的
./public/mockServiceWorker.js
npx msw init public/
使用 MSW
目录结构和文件解释
MSW SSE Mock 项目 - 目录结构
===============================
项目根目录: /mock-sse-by-msw
├── 📁 public/ # 静态文件目录
│ └── 📄 mockServiceWorker.js # MSW Service Worker 脚本,拦截浏览器请求
├── 📁 src/ # 源代码目录
│ ├── 📁 app/ # Next.js App Router 目录
│ │ ├── 📄 globals.css # 全局 CSS 样式,应用于整个应用程序
│ │ ├── 📄 layout.tsx # 根布局组件,包住所有页面
│ │ └── 📄 page.tsx # 主页面组件,展示 SSE 功能演示
│ ├── 📁 components/ # 可复用的 React 组件目录
│ │ └── 📄 mockServer.tsx # Mock 服务器初始化组件
│ └── 📁 mock/ # MSW Mock 配置和处理器
│ ├── 📁 __fixture__/ # 测试数据目录
│ │ └── 📄 summaryTexts.js # SSE 流式传输模拟的示例文本数据
│ ├── 📄 browser.ts # MSW 浏览器端设置,用于拦截客户端请求
│ ├── 📄 handler.ts # MSW 请求处理器,定义 Mock API
│ ├── 📄 initmock.ts # 开发环境 Mock 初始化逻辑
│ └── 📄 server.ts # MSW 服务器端设置,用于 Node.js 环境(测试)
└── 📄 yarn.lock # Yarn 依赖锁定文件,用于包管理
开发工作流
- MSW Service Worker 在浏览器中拦截网络请求
- Mock 处理器提供真实的 API 响应
- SSE 端点逐字符流式传输数据
- React 组件消费实时数据流
- 开发服务器提供热重载即时更新
- 更改
./src/mock/__fixture__/summaryText.js中的 Markdown 数据
- 更改
核心代码
在 MSW 中模拟 SSE (Server-Sent Events) handler
// ./src/mock/handler.ts
export const handlers = [
// SSE (Server-Sent Events) handler
http.get("/api/sse", ({ request }) => {
const url = new URL(request.url);
const interval =20;
const summaryKey = url.searchParams.get('summary');
const maxCount = summaryKey && summaryArray[summaryKey].length || 10;
const stream = new ReadableStream({
start(controller) {
let counter = 0;
const sendEvent = () => {
let message = "";
// If summary key is provided and exists in summaryArray, use summary text
if (summaryKey && summaryArray[summaryKey] && summaryArray[summaryKey][counter]) {
message = summaryArray[summaryKey][counter];
}
const data = {
id: counter,
message,
timestamp: new Date().toISOString(),
type: 'update',
};
const eventData = `data: ${JSON.stringify(data)}\n\n`;
controller.enqueue(new TextEncoder().encode(eventData));
counter++;
if (counter < maxCount) {
setTimeout(sendEvent, interval); // Send event with custom interval
} else {
// Send final event and close
controller.enqueue(new TextEncoder().encode('data: {"type":"close","message":"Stream ended"}\n\n'));
controller.close();
}
};
// Send initial event
sendEvent();
}
});
return new HttpResponse(stream, {
status: 200,
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Cache-Control'
}
});
})
]
复杂格式 Markdown 的输出效果
| 内置的 CSS 样式 | GitHub 风格的 Markdown | Code Block |
|---|---|---|
| 复杂的数学公式 | 未闭合的 Markdown | Mermaid Diagram |
|---|---|---|
代码仓库
Check out github.com/itpretty/mo… and have fun as always :)