别再手敲 JSON 造数据了!Mock 数据生成器实战与避坑指南

前几天,群里有朋友问我:"你们前后端并行开发的时候,接口还没好,前端那几十条测试数据是怎么来的?"

说实话,我之前也被这个问题坑过不少次。

最夸张的一次:一个订单列表页,我手写了 200 条 JSON 数据。写到第 80 条的时候后端说字段名要从 orderNo 改成 order_no,改完又说要加个 refundAt,再改完又说要支持分页。

一份"临时用一下"的假数据,我前后维护了大半天。

那一刻我才意识到:手写 Mock 数据不是慢的问题,是它会持续偷走你的时间。

问题根源:手写 Mock 数据的三宗罪

手写 JSON 看着简单,实际上埋了三类坑。

🕳️ 罪一:假得太干净

手敲的数据永远是 "name": "test1""remark": "aaa""amount": 100

结果就是:所有边界情况都测不出来。空字符串会不会崩?null 怎么处理?字段超长 200 字符会不会撑破布局?带 emoji 的商品名会不会乱码?金额为 0 和为负的时候页面长什么样?

这些 Bug 全都安安静静地活到了测试环境,甚至活到了线上。

🕳️ 罪二:结构漂移

后端改了字段,没人会记得去同步那份手写的 mock.json

于是前端拿着过期的数据结构自测,一路绿灯,联调当天集体翻车。

🕳️ 罪三:不可复现

随手写的随机数据,出 Bug 了想复现------数据已经变了。

"你刚才那个报错还在吗?" "我刷新了一下,没了。"

这种对话,做过前端的都懂。

核心结论:Mock 数据的价值不在"有数据",而在"数据结构可控、内容可复现、边界可覆盖"。

Mock 数据的四个层次

我把常见做法分成四层,越往上越接近工程化。

层次 做法 适用阶段 主要问题
L1 静态手写 直接敲 JSON 文件 原型、5 条以内 假得干净、难维护
L2 模板随机 faker / Mock.js 批量生成 联调、demo 结构与接口无契约
L3 Schema 驱动 JSON Schema → 数据 正式项目 需要先有 Schema
L4 场景化 Mock MSW / Mock Server / 录制回放 单测、E2E、长期维护 有一定接入成本

大多数团队卡在 L1 和 L2 之间,而真正省事的是 L3:契约即数据源。接口 Schema 一改,Mock 数据自动跟着变,结构漂移的问题从根上消失。

方案对比:该用哪个

方案 类型 优势 局限
@faker-js/faker JS 库 数据真实、支持中文、可设种子 需要写代码
Mock.js JS 库 模板语法直观、前端拦截请求 语法私有、生态偏老
json-schema-faker JS 库 Schema 驱动、契约一致 依赖已有 Schema
MSW 拦截层 单测/浏览器通吃、贴近真实请求 有接入成本
在线生成器 Web 工具 零安装、秒出结果 只能造扁平结构
数据库造数脚本 SQL/Python 能造关联数据、贴近真实分布 最重

没有银弹。临时用 → 在线工具或 faker;正式项目 → Schema 驱动 + MSW。

实战一:@faker-js/faker(推荐日常主力)

Faker 是目前 JS 生态里最活跃的选择(本文用的是 v10.6.0)。v9 之后 API 做过一轮规整,address 改名 locationname 改名 person,中文 locale 也够用。

bash 复制代码
npm i -D @faker-js/faker
js 复制代码
import { fakerZH_CN as faker } from '@faker-js/faker';

// 关键:设种子,保证每次生成的数据完全一致,Bug 可复现
faker.seed(20260914);

// 中文 locale 下 phone.number() 给的是固话格式(如 0747-95502468),
// 不是 11 位手机号。要手机号得自己拼号段:
const SEGMENTS = ['133', '150', '176', '186', '199'];
const mobile = () => faker.helpers.arrayElement(SEGMENTS) + faker.string.numeric(8);

const orders = Array.from({ length: 200 }, (_, i) => ({
  id: faker.string.uuid(),
  orderNo: `SO${String(i + 1).padStart(8, '0')}`,
  userName: faker.person.fullName(),
  phone: mobile(),
  // 金额用「分」存整数,别用浮点,0.1 + 0.2 的坑不要踩第二次
  amountCents: faker.number.int({ min: 0, max: 5_000_000 }),
  status: faker.helpers.arrayElement(['pending', 'paid', 'shipped', 'refunded']),
  remark: faker.helpers.arrayElement([null, '', faker.lorem.sentence(), '备注含 emoji 🎉']),
  createdAt: faker.date.recent({ days: 30 }).toISOString(),
}));

console.log(JSON.stringify(orders, null, 2));

⚠️ 上面这段我实际跑过(Faker v10.6.0)。fakerZH_CN.phone.number() 输出的是 0747-9550246814917446540 这种混合结果,拿去做手机号校验的接口会被直接打回。这是个很容易忽略的细节------大家默认中文 locale 就该出中文手机号,其实不是。

⚠️ 注意 remark 那行------我故意把 null、空串、正常文本、emoji 混在一起。这才是 Mock 数据该有的样子:主动制造脏数据。

常用 Faker 片段速查(v9+,以下均实测可运行)

js 复制代码
faker.seed(20260914);                              // 固定种子,必须放最前面
faker.person.fullName();                           // 中文姓名
faker.internet.email();                            // 邮箱
faker.location.city();                             // 城市
faker.string.uuid();                               // UUID
faker.number.int({ min: 0, max: 5_000_000 });      // 区间整数
faker.number.float({ min: 0, max: 100, multipleOf: 0.01 }); // 保留两位小数
faker.date.between({ from: '2026-08-01', to: '2026-09-14' });
faker.helpers.arrayElement(['active', 'frozen']);  // 枚举随机
faker.helpers.multiple(() => ({ /* ... */ }), { count: 200 }); // 批量生成
faker.string.numeric(8);                           // 8 位数字串(拼手机号用)

⚠️ fakerZH_CN.phone.number() 不要直接当手机号用,详见下一节。

实战二:Mock.js(模板语法最直观)

如果你的团队已经在用 Mock.js,它的模板语法确实写得快。

js 复制代码
const Mock = require('mockjs');

const data = Mock.mock({
  'list|50': [{
    'id|+1': 1,
    name: '@cname',
    'age|18-60': 1,
    'level|1': ['VIP1', 'VIP2', 'VIP3'],
    email: '@email',
    'amount|1-10000.2': 1,
    createdAt: '@datetime("yyyy-MM-dd HH:mm:ss")',
    avatar: '@image("80x80", "#4A90E2", "#FFF", "png", "U")',
  }],
});

'list|50' 表示生成 50 条,'id|+1' 表示自增,'level|1' 表示从数组里随机取一个。

⚠️ 这里有个我实测踩到的坑@datetime 生成的时间跨度是 1970 年到现在。我跑了 400 次统计,只有 9.5% 的日期落在 2020 年之后------也就是说你造 100 条"最近订单",会有 90 条的时间是 1988 年、2003 年这种。

页面上按时间倒序一排,全是十几年前的订单,一眼就假。而且如果业务逻辑依赖"近 7 天",这些数据直接测不到东西。

正确写法是指定区间:

js 复制代码
// 用 Mock.Random.date 限定范围,别用 @datetime
const d = Mock.Random.date('2026-08-01', '2026-09-14');   // 2026-08-01

局限也很明显:语法是私有的,Schema 换不过来,而且它默认会拦截 XHR------上线前一定要确认 mock 代码没被打进生产包。

另外 @cname@email 这类占位符生成的是"格式对但内容假"的值(比如 r.pordsdc@vdemckts.name),域名叫 vdemckts.name,一看就不是真邮箱。做前端展示没问题,但如果你的测试逻辑会真的发信或做域名白名单校验,就会炸。

实战三:json-schema-faker(Schema 驱动,最工程化)

这是我最推荐的正式项目做法。前提是你有接口的 JSON Schema(没有的话,可以让后端从 DTO 生成,或者用工具从样例 JSON 反推)。

⚠️ 先说一个会直接让你卡住的问题:网上的教程大多过时了。

搜到的写法基本都是 const { JSONSchemaFaker } = require('json-schema-faker'),然后 JSONSchemaFaker.generate(schema)。我在 v0.6.3 上实测,两行都跑不通:

  • require() 直接抛 ERR_PACKAGE_PATH_NOT_EXPORTED------新版本只发 ESMexports 字段里只有 import,没有 require
  • 没有 JSONSchemaFaker 这个命名导出了,改成了一组扁平函数:generate / generateSync / registerFormat / define

正确写法:

js 复制代码
// 注意:ESM,且是 generateSync 不是 JSONSchemaFaker.generate
import { generateSync } from 'json-schema-faker';

const schema = {
  type: 'object',
  required: ['orderId', 'amount', 'status'],
  properties: {
    orderId: { type: 'string', format: 'uuid' },
    amount: { type: 'integer', minimum: 0, maximum: 5000000 },
    status: { type: 'string', enum: ['pending', 'paid', 'shipped', 'refunded'] },
    couponId: { type: ['string', 'null'] },
    paidAt: { type: ['string', 'null'], format: 'date-time' },
  },
};

const order = generateSync(schema);
// => {"orderId":"a08ff49b-6f77-4263-8716-c43069c43a8c","amount":964070,
//     "status":"shipped","couponId":null,"paidAt":"2010-05-08T03:43:34Z"}

const orders = Array.from({ length: 200 }, () => generateSync(schema));

如果你的项目还是 CommonJS,只能锁老版本(json-schema-faker@0.5.x)或者改成 ESM。这个坑我踩了小半天,因为报错信息 No "exports" main defined 完全没提示你是模块格式问题。

好处:Schema 是唯一事实来源。后端改字段 → Schema 变 → Mock 自动变,结构漂移彻底消失。同一份 Schema 还能拿去做接口文档和响应校验,一份投入三处收益。

🎯 顺带一提,从上面那段真实输出能看出 Schema 驱动的价值:couponId 自动生成了 nullamount 自动落在 minimum/maximum 区间内,status 自动从 enum 里取值。这些约束条件写在 Schema 里,生成的数据天然就是合法的,不用像手写那样靠人脑记"这个字段可以为空"。

反过来也要注意:paidAt 生成的是 2010-05-08,因为 Schema 里没写时间范围。Schema 只保证"格式合法",不保证"业务合理" ------需要近期时间的话,得自己加 format 扩展或后处理。

实战四:在线生成器(临时用,别当主力)

如果只是要一批扁平结构的数据,贴进 Postman 或者塞进 demo 页面,我一般懒得开编辑器写脚本。

我自己用的是这个纯前端的小工具:Mock 数据生成器。用法很直白------配字段名和类型,填行数,点生成,复制或下载 JSON。支持的类型有 nameemailphoneaddressdateuuidurlipnumberbooleancustom

https://leowh.com/tools/mock-data/index.html

两个细节我觉得设计得挺对:

  • custom 支持 值A|值B|值C 这种竖线分隔的候选文本,枚举字段直接搞定
  • number 可以设最小值和最大值,区间数据不用自己算

它是纯前端实现,配置和数据都在浏览器本地生成,不往服务器发------对内网环境或者字段本身敏感的场景友好一些。

但边界要讲清楚,别拿它当主力:

  • 🔴 只能造扁平对象数组,不支持嵌套对象、外键关联、条件依赖(比如 status = refunded 时必须有 refundAt
  • 🔴 每次生成都是随机的,没有种子机制,Bug 不可复现
  • 🟡 造完就是一坨静态 JSON,接口字段一改又得重来一遍

所以我的分工是:demo 和联调冒烟用在线工具,正式测试数据和单测夹具回到代码里写(faker / Schema 驱动)。

顺带一提,同一站还有个 JSON 转实体类工具,把生成好的 JSON 贴进去能直接出 Java / C# / Go / TypeScript / Python 的类定义。造数据 → 看结构 → 生成实体,在接口文档还没写全的阶段,这条链路能省点事。

实战五:MSW(让单测吃到真实请求链路)

到了 L4,重点是"拦截"而不是"生成"。MSW(Mock Service Worker)在 Service Worker 层拦请求,单测和浏览器调试用同一套 handler。

bash 复制代码
npm i -D msw
js 复制代码
// mocks/handlers.js
import { http, HttpResponse } from 'msw';
import { fakerZH_CN as faker } from '@faker-js/faker';

faker.seed(42);

const makeOrders = (n) =>
  Array.from({ length: n }, () => ({
    id: faker.string.uuid(),
    amountCents: faker.number.int({ min: 0, max: 5_000_000 }),
    status: faker.helpers.arrayElement(['pending', 'paid', 'shipped', 'refunded']),
  }));

export const handlers = [
  http.get('/api/orders', ({ request }) => {
    const url = new URL(request.url);
    const page = Number(url.searchParams.get('page') ?? 1);
    return HttpResponse.json({ page, total: 200, list: makeOrders(20) });
  }),

  // 主动造异常,这才是 Mock 最大的价值
  http.get('/api/orders/:id', () =>
    HttpResponse.json({ code: 500, msg: 'internal error' }, { status: 500 })),
];

🎯 重点:Mock 不只是造正常数据,更是造异常。 500、超时、限流 429、字段缺失、字段类型错误------这些场景在真实环境里很难稳定复现,但在 Mock 层里一行代码的事。

避坑清单

这部分是我踩过的坑,按优先级排。

🔴 必须注意

  1. 一定要设随机种子。 faker.seed(n) 写死,Bug 才能复现。不设种子的随机数据等于没数据。
  2. 金额别用浮点。 用整数「分」或者 decimal 字符串。amount: 19.99 这种写法迟早出事。
  3. 主动造脏数据。 每个可空字段都要覆盖 nullundefined'';字符串字段要覆盖超长(200+ 字符)、emoji、HTML 特殊字符、换行。
  4. Mock 代码不能进生产包。 Mock.js 会拦 XHR,MSW 的 worker 文件要按环境注入。上线前搜一遍构建产物。
  5. 时间字段统一格式。 到底是 ISO 8601(2026-09-14T10:00:00.000Z)、时间戳、还是本地时间字符串?团队内定死一种,Mock 数据必须和后端一致,尤其注意时区。

🟡 建议注意

  1. 敏感数据合规。 手机号、身份证、银行卡一律用生成的虚构值,绝对不要从生产库导真实数据当 Mock。Faker 生成的手机号是合法号段格式但不对应真实用户,仍然建议只在内网/测试环境流转。
  2. 别一次造太多。 前端一次渲染 10 万条 JSON,浏览器先卡死给你看。列表页造 50--500 条足够,压测数据另说。
  3. 关联数据要有外键一致性。 order.userId 必须能在 users 里找到,否则测关联查询全是假阳性。这类需求 faker 也能做,但要自己维护 ID 池。
  4. 枚举值从后端常量同步。 手写 ['pending', 'paid'] 很容易漏掉后端新增的状态,最好从共享的常量文件或 Schema 的 enum 读。
  5. 数据分布要像真的。 全是 status: 'paid' 的订单列表,测不出状态筛选逻辑。按真实比例分配(比如 60% paid、20% pending、15% shipped、5% refunded)。

什么情况下用哪种方案?

🔴 直接上 Schema 驱动(json-schema-faker + MSW)

  • 项目会长期维护,多人协作
  • 已经有或愿意维护接口 JSON Schema
  • 需要写单元测试 / E2E 测试
  • 接口字段变动频繁

🟡 用 faker / Mock.js 写脚本

  • 项目中等规模,没有 Schema
  • 需要造有一定业务逻辑的数据(关联、分布、条件依赖)
  • 数据需要可复现(配合种子)

🟢 用在线生成器

  • 一次性 demo、临时联调、教学示例
  • 扁平结构、几百条以内
  • 不想装依赖、不想写脚本

常见问题

Q:Mock 数据和后端真实数据不一致怎么办?

A:根源是没有单一事实来源。最好的解法是 Schema 驱动------后端 DTO 生成 Schema,前端从 Schema 生成 Mock,同时用 Schema 做响应校验。退一步的做法是引入契约测试(如 Pact),至少能在 CI 里发现漂移。

Q:为什么我造的数据测不出 Bug,一到测试环境就炸?

A:因为你的数据太干净了。检查三件事:有没有覆盖 null / 空串 / 字段缺失?有没有超长字符串和特殊字符?有没有造过异常响应(500 / 超时 / 429)?

Q:随机种子设多少合适?

A:随便,但要写死并记在代码注释里,比如 faker.seed(20260914)。需要复现某个 Bug 时,把当时用的种子固定下来,写成一个测试用例。

Q:能用生产库的数据脱敏后当 Mock 吗?

A:技术上可以,但成本高且风险大------脱敏不彻底就是数据泄露事故。除非你在做「数据分布真实性」要求极高的场景(比如推荐算法),否则用生成式数据更安全。

Q:Mock.js 和 MSW 能一起用吗?

A:不建议。两者都会拦请求,容易互相干扰导致排查困难。选一个,团队统一。

Q:在线生成器造的数据能直接用于自动化测试吗?

A:不适合。它没有种子机制,每次生成结果不同,测试会变成 flaky test。自动化测试的数据必须由代码生成并固定种子。

总结

Mock 数据这件事,本质是从「手工作坊」升级到「流水线」:

L1 → L2 :别手敲 JSON 了,用 faker / Mock.js 批量生成,效率提升一个数量级

L2 → L3 :引入 JSON Schema,让契约成为唯一事实来源,根治结构漂移

L3 → L4 :用 MSW 统一拦截层,让单测和联调共用一套 Mock,并且开始主动造异常

贯穿始终:设种子保证可复现、主动造脏数据覆盖边界、金额用整数、Mock 不进生产包

⚠️ 特别提醒: 如果你的 Mock 数据永远只有"正常值",那它测出来的"通过"没有任何意义。

工具选型上,我的习惯是分场景:正式项目走 Schema 驱动 + faker,临时 demo 和联调冒烟用在线生成器图个快。两者不是替代关系。

相关推荐
Luminbox紫创测控1 小时前
航天热平衡试验用太阳模拟器:光学积分器设计与均匀性提升方法
测试工具·安全性测试·太阳光模拟器·测试标准·航天热平衡实验
程序员杰哥6 小时前
UI自动化测试:Jenkins配置
自动化测试·软件测试·python·测试工具·职场和发展·jenkins·测试用例
TomEval7 小时前
【性能测试】17 - 性能测试入门
测试工具·jmeter
wuhuhuan9 小时前
Case Generator 测试自动化平台
python·测试工具·自动化
星核0penstarry1 天前
ToolGrad:把数据生成倒过来,工具调用样本通过率提到 99.8%
人工智能·测试工具·llm·函数调用·数据合成·文本调用
测试19982 天前
Jmeter接口自动化测试:Jmeter变量的使用
自动化测试·软件测试·测试工具·jmeter·职场和发展·测试用例·接口测试
Tinyfacture3 天前
postman抓取登录鉴权码token及设置全局变量
测试工具·postman
川石课堂软件测试3 天前
涨薪技术|Prometheus之HTTP API中使用PromQL
网络协议·测试工具·jmeter·http·单元测试·postman·prometheus