当后端还没准备好、而前端又等不起时,模拟数据 API 就是答案。Mock API 让前端团队可以基于稳定的契约交付 UI,而真实后端可以慢慢赶上;让移动端开发者无需真实授权令牌就能调试应用;也让任何人——尤其是 QA 团队——不必等待预发布环境部署就能测试新功能。本教程将介绍三种能力逐级增强的 Mock 模式,最后演示如何直接把 USA Data Tools REST API 当作模拟后端使用。

为什么需要 Mock API?

  • 并行工作:前后端团队约定好契约;前端 Mock 它,后端实现它,双方交付都更快。
  • 确定性:稳定的 Mock 每次运行都返回相同的数据,消除不稳定的测试。
  • 边缘情况:Mock 可以刻意返回 500 和 422,让你的错误处理代码变得可测试。
  • 隐私:迭代过程中没有真实客户数据离开生产环境。

模式一——静态托管的 JSON 文件

最简单的 Mock 就是一个由任意静态 HTTP 服务器托管的 JSON 文件目录。它适合只读端点。

mock-api/
  users.json
  users.42.json
  addresses.json

用你喜欢的任何工具托管——Python 的 http.server、VS Code Live Server,或一行命令的 Vite 静态目录。前端代码通过环境变量请求 /mock-api/users.json 而不是 /api/users,这样之后可以切换基础 URL。

const API_BASE = process.env.REACT_APP_API_BASE || "/mock-api";
const users = await fetch(`${API_BASE}/users.json`).then(r => r.json());

局限:没有单条记录端点、没有分页、没有 PUT/POST。这种模式能覆盖 70% 的前端工作(列表和详情页),然后就力不从心了。

模式二——一个轻量 Node/Express 服务器

对于剩下的场景,写一个 50 行的 Express 服务器,用真实的 HTTP 语义返回夹具数据。状态码、查询参数解析和幂等的 POST 处理都免费获得。

// mock-server/server.js
import express from "express";
import { readFileSync } from "node:fs";

const users = JSON.parse(readFileSync("./fixtures/users.json", "utf8"));
const addresses = JSON.parse(readFileSync("./fixtures/addresses.json", "utf8"));

const app = express();
app.use(express.json());

// 带分页 + 搜索的列表
app.get("/api/users", (req, res) => {
  const page = Number(req.query.page ?? 1);
  const size = Number(req.query.size ?? 20);
  const q = (req.query.q ?? "").toLowerCase();

  const filtered = users.filter(u =>
    !q || u.name.toLowerCase().includes(q) || u.email.includes(q)
  );

  const start = (page - 1) * size;
  const data = filtered.slice(start, start + size);

  res.json({
    data,
    page,
    size,
    total: filtered.length,
  });
});

// 单条记录
app.get("/api/users/:id", (req, res) => {
  const user = users.find(u => u.id === Number(req.params.id));
  if (!user) return res.status(404).json({ error: "not found" });
  res.json(user);
});

app.listen(8081, () => console.log("Mock API on http://localhost:8081"));

这个实现为前端团队提供了真实的 HTTP 语义——查询参数、状态码、content-type——代价只是大约一小时的敲键盘时间。

模式三——持续生成新鲜数据的 Mock

静态夹具会过时;上个 Sprint 的 10 条测试数据已经无法测出你刚做的无限滚动分页。使用 USA Data Tools API 按需为你的 Mock 填充数据。

// 每次 Mock 服务器启动时获取 50 条新鲜的美国记录
async function seedAddresses(count = 50) {
  const r = await fetch(
    `https://vic999.com/us-address/api/v1/address?count=${count}`,
    { headers: { Authorization: `Bearer ${process.env.USA_KEY}` } }
  );
  if (!r.ok) throw new Error(`seed failed: ${r.status}`);
  const { data } = await r.json();
  return data.map((a, i) => ({ id: i + 1, ...a }));
}

let cache = [];
app.get("/api/addresses", async (req, res) => {
  if (cache.length === 0) cache = await seedAddresses(50);
  res.json({ data: cache });
});

第一个请求到来时,Mock 会懒加载 50 条真实地址(邮编+州地理一致、区号有效的电话),然后从缓存中提供服务。重启 Mock 就能获得一批新数据——当你想要默认确定性、但每个 Sprint 又想要不同数据形态时非常有用。

提供边缘情况的响应

一个永远只返回 200 OK 的 Mock 测不出错误路径。用一个查询参数做分支,让测试可以选择进入失败模式:

app.get("/api/users/:id", (req, res) => {
  if (req.query._force === "500") {
    return res.status(500).json({ error: "forced_error" });
  }
  if (req.query._force === "slow") {
    return setTimeout(() => res.json(users[0]), 5000);
  }
  const user = users.find(u => u.id === Number(req.params.id));
  if (!user) return res.status(404).json({ error: "not_found" });
  res.json(user);
});

现在你的前端测试可以请求 ?_force=500 来验证错误边界 UI 是否出现,或用 ?_force=slow 验证加载指示器是否显示。仅此一个模式就能堵住大多数前端的健壮性缺口。

为 Mock 契约编写文档

Mock 最大的成本是与真实后端之间的漂移。为 Mock 服务器配一份 OpenAPI 规范——哪怕手写——并双向验证:

  1. 从规范生成 Mock 响应(运行时返回符合 Schema 的数据)。
  2. 在 CI 中用规范校验真实后端(一个便宜的契约测试)。

一旦 Mock 与真实后端使用同一套 Schema,前端不在乎自己正在跟哪一个对话。

npx @stoplight/spectral-cli lint openapi.yaml --ruleset spectral:oas

接入 CI

jobs:
  front-end-e2e:
    runs-on: ubuntu-latest
    services:
      mock:
        image: node:20
        options: --entrypoint node
        env:
          USA_KEY: ${{ secrets.USA_KEY }}
        ports: ["8081:8081"]
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run start:mock &
      - run: npx playwright test
      - run: npx @stoplight/spectral-cli lint openapi.yaml

Mock 作为服务启动,前端测试针对它运行,契约检查会在规范漂移时发出警告。当真实后端就绪后,切换环境变量即可——只要契约是诚实的,同样的测试就能通过。

三种方案如何取舍

模式最适合成本
静态 JSON只读的列表/详情 UI成本最低
轻量 Express 服务器POST/PUT、分页、搜索一个下午
USA API 填充数据真实的大数据集API 密钥 + 50 行代码

跨团队交接

当后端团队准备就绪时,前端的 Mock 就变成了回归测试套件。经过 spectral 校验的规范成为契约;前端的 Playwright 套件针对真实后端运行,暴露任何漂移(“后端新增了必填字段”)。这就是为什么契约比 Mock 更重要——但 Mock 让你从第一天起就能开始检验契约。

提示:Mock 服务器天然是短命的。当真实后端上线后,不要扔掉 Mock——让它继续在仅本地可达的端口上运行,用于快速的离线迭代,以及故意触发那些真实后端不会按需产生的失败场景。

全新 SaaS 项目的实用配方

如果你从零开始:

  1. 为你需要的前 5 个端点编写 OpenAPI 规范。
  2. 启动由 USA Data Tools 地址 API 填充数据的 Express Mock 服务器。
  3. 通过 API_BASE 环境变量把前端接上去。
  4. 添加 ?_force= 技巧,用于慢速/500/超时场景。
  5. 在 Mock 和真实后端的 CI 中都运行 spectral lint 作为契约测试。

这套模式在我们的项目中经受了多年的考验,可以从一个前端开发者扩展到 20 人的团队,而无需改变架构。

总结

模拟数据 API 不是走捷径——它是团队之间的交付契约,让所有人都能并行推进。先用几个 JSON 文件起步,需要 HTTP 语义时升级到 Express 服务器,再用地址生成器填充真正能测出边缘情况的数据集。搭配一个经过 Spectral 校验的 OpenAPI 规范,等真实后端就绪时,同一套测试无需任何重写即可直接运行。