☰
测试数据生成实战:从Faker到状态机,造出真实可用的仿真数据
2026/10/7 4:40:17 网站建设 项目流程

“随便造点数据”这个标题,一看就是踩过坑的人才会写出来的。真到了联调、压测、做Demo、跑报表的阶段,你会发现最缺的往往不是代码,也不是服务器,而是数据。开发环境里连个正经用户都没有,登录功能怎么验?订单列表空空荡荡,分页怎么调?没有任何一笔支付记录,对账逻辑怎么跑?于是我们开始“随便造点数据”。但这两个字往往是最贵的——数据造得不够真,业务逻辑里的坑根本炸不出来;造得太随意,上线前又得花几倍时间返工。这篇文章记录的是我在造数据这件事上攒下来的一些经验,包括工具选型、字段设计、批量生成、边界值处理,以及几个让我印象深刻的翻车现场。适合正在为测试环境、Demo演示或者数据脱敏发愁的人,哪怕你完全没写过造数脚本,照着思路也能拼出一个能用的。

1. 为什么“随便造点数据”这件事,值得认真对待

1.1 没有数据,开发进度卡在最后一公里

很多项目在开发阶段是“逻辑先行”的。接口写好了、页面调通了,结果一点进详情页就是空指针或者空列表。不是代码有问题,而是数据库里根本没有对应数据。你花半小时造了一条数据,点过去,发现列表能显示了,但详情页的字段没配全,又得回头补齐。这种反复在开发期特别费时间,尤其是前后端并行的时候,前端拿着假接口联调,后端测着自己的逻辑,两边用的根本不是同一份数据,最后对不上,又是一轮扯皮。

所以后来我的态度是:造数据不是测试阶段的活,而是开发期就该同步做的事。越早把一份“长得像生产环境”的数据放进开发库,很多接口取值、空值判断、格式化问题就能提前暴露。这个成本比你部署到测试环境再发现要低得多。

1.2 生产数据不是你想用就能用

有的人会说,直接复制一份生产数据到测试库不就行了?听起来简单,但问题一堆。首先是隐私合规压力,电话、身份证、地址、支付记录,这些敏感信息在测试环境里裸奔,出了事很难解释。其次是数据量太大,生产库几百G,测试库根本不需要那么多数据,导过来还会拖慢开发机的性能。第三是数据特征不同,生产环境里有复杂的状态流转、异常订单、历史遗留数据,直接复制确实会带来一些“惊喜”,但也会带来一堆和需求无关的脏数据。

与其从生产库里费劲清洗、脱敏,不如专门造一份“高度仿真但完全虚构”的数据。这个方案可控性强,想要多少量就给多少量,想包含什么边界值就加什么值,对开发、测试、前端联调、Demo演示都是最舒服的。

1.3 造数据不是“乱填”,而是“定向模拟”

“随便造”这三个字,我理解的是不拘泥于严格业务数据,但绝不是完全乱来。比如你造一个用户表,用户名乱填成“张三”“李四”没问题,但手机号也得能过正则校验,不然登录模块永远测不出来。订单金额乱填成负数可以,但你的对账逻辑到底该不该拦截负数,是不是你想测的点?造数据前最好先想一遍:这条数据会被哪些逻辑消费?它需要满足什么约束?它要触发哪条分支?

想清楚了,造数据就是从“填空”变成“建模”,造出来的数据才能真正帮你发现问题,而不是只让页面不报错。这也是整篇文章最核心的一句话。

2. 造数据的第一层:把基础字段“感觉到位”

2.1 字符串、整数、日期:最基础的三种随机

很多人第一次写造数脚本,就是从随机数开始。Python里的random模块足够应付基础场景,但有几个细节容易出错。比如random.randint(0, 999999)生成订单号,看着没问题,但订单号如果是字符串类型,前端展示可能缺位补零;如果订单号需要唯一,随机碰撞的概率在数据量大的时候就不能忽略。

我通常会根据字段语义拆分:

  • 字符串类型:从一组预定义前缀加随机数字生成,类似ORDER-20250101-001这种带日期和中序号的结构,可读性和唯一性都比纯随机好。
  • 整数类型:区分业务含义。年龄一般在1到100之间,金额要考虑小数位和精度,库存要考虑最小值不能为负。
  • 日期和时间:用datetime模块做偏移,比如注册时间取过去365天内的随机日期,最后登录时间不能早于注册时间。

基础字段的“感觉对”体现在类型、范围、格式三个维度上都说得通。先写一个小工具函数,把这些规则收敛起来,后面再造数就很顺手了。

2.2 手机号、身份证、邮箱这类“有校验规则”的字段

这里是最容易翻车的地方。手机号不是纯随机11位数字就行的,它得符合号段规则,否则一入你的项目里做了运营商号段校验,这堆数据就全部失效。身份证更复杂,前6位对应地区,中间8位是出生日期,第17位有性别含义,最后一位可能是X,而且整体还要满足校验码规则。邮箱要保证格式合法,最好能按域名分布,比如一部分用qq.com,一部分用163.com,一部分用公司自建域名。

我的建议是不要去手动拼这些复杂字段,直接用Faker这类成熟库,它已经内置了各个国家地区的手机号、身份证、地址、公司名、邮箱等生成规则。用库的好处不只是省时间,更重要的是它生成的规则是经过大量使用者验证的,不容易出现低级格式问题。

2.3 第一步先搞出一版“有感觉”的数据

把基础字段规则整理好之后,不要急着追求数据量,先造100条出来,肉眼扫一遍。看看姓名和性别有没有明显不搭配,订单日期是不是有未来的日期,金额有没有出现超过正常范围的极端值。这步很像做饭时候的试味,先小批量验证规则,确认没问题再上量。

我自己的习惯是先把生成逻辑写成一个函数,输入是数量,输出是List[Dict],每一步都可以打印到终端看看。这样后续不管接CSV、Excel、MySQL还是PostgreSQL,都只是把结果写出去的事,和生成逻辑本身解耦。

3. 造数据的第二层:字段之间要有“生活常识”

3.1 数据之间的关联关系,才是“以假乱真”的关键

基础字段只是及格线。真正让人觉得“这套数据没毛病”的,是字段之间的逻辑关系。举个最常见的例子:用户表里有注册时间和最后登录时间,如果最后登录时间随随便便随机,很容易出现用户还没注册就已经登录了的鬼故事。订单表里的下单时间和支付时间,必然不能早于下单时间;发货时间不能早于支付时间。用户表和订单表通过用户ID关联,那同一个用户的订单金额分布应该符合这个用户的消费水平。

这些关联关系看着简单,但很多造数脚本就是死在这上面。原因也容易理解:写造数脚本时,人的注意力全在单字段的随机范围上,很少去画一张字段依赖图。等数据一多,各种前后矛盾就出来了。

3.2 用状态机思路来造“过程型”数据

如果业务里有状态流转,比如订单从“待支付”到“已支付”再到“已发货”“已完成”,造数就需要状态机思路。一个订单在时间轴上是有生命周期的,不能一个状态为“已完成”的订单,支付时间比下单时间还早。我习惯先定义状态之间的合法转换路径,然后照着路径去生成时间戳和状态字段。

比如想生成一批已完成订单,就按“下单-支付-发货-完成”四个节点依次生成时间,每个后续时间在前一个时间上增加随机间隔。间隔也要符合业务常识:支付一般在下单后几分钟到一天内,发货在支付后几小时内到两天内,完成在发货后几天内。用这种思路造出来的数据,拿来画漏斗、看时长分布,都是成立的。

3.3 数据分布:真实业务不是均匀大乱炖

纯随机生成的分布,和真实业务分布差得非常远。比如用户活跃度,真实场景肯定是少数用户很活跃、大量用户偶尔访问一次,也就是长尾分布。订单金额往往在低价区扎堆,高价订单很少。如果你尝试用random.uniform(100, 10000)生成金额,做出来的客单价分布会跟实际业务对不上,后续做数据分析展示时就特别假。

这里有两个常用手段。一个是按比例分层抽样:先设定好业务比例,比如订单金额,20%是0到100,50%是100到1000,25%是1000到5000,5%是5000以上,再在每层里随机。另一个是用正态分布或者幂律分布来模拟自然分布,取决于你的业务形态。前者更简单直观,适合大多数内部测试场景;后者更贴近真实生态,适合做数据产品Demo或者算法验证。

4. 造数据的第三层:批量生成、写入性能与数据量级

4.1 十万条数据跑得动吗?先想清楚瓶颈在哪

数据量上到十万、百万级别,你很快会发现,生成数据本身一点也不慢,慢的是两件事:内存占用和数据库写入。Python里一次性生成一百万个字典对象放进列表,内存轻松吃掉好几个G,机器差点就直接卡死了。解决思路也简单:不要一次性生成全量,改成生成一批、写一批、清一批。比如每5000条刷一次数据库,循环反复,内存占用就能打得很低。

还有一个常被忽略的点:数据库批量插入要用批量SQL,不要一条一条insert。一条条插入,每次都有网络往返和事务提交开销,十万条能跑死人。批量insert每5000条提交一次,时间能从几分钟降到几秒,差异非常明显。

4.2 生成唯一键的时候,别踩并发和碰撞的坑

造数脚本一般单线程跑,碰撞问题不明显,但只要你用到唯一键,还是建议留个心眼。直接用random生成然后去重,数据量一大就可能反复碰撞。更好的是采用“前缀+递增序号”的方式,或者用UUID。用UUID唯一性没问题,就是索引空间大,如果业务主键是字符串类型能接受,如果是整型主键,UUID就不合适了。

我自己经常用“业务前缀+日期+循环序号”的结构,比如user_20250110_0001。这样做既保证可读性,又天然唯一,而且对后续按前缀模糊查询也友好。注意这个方案只适合造数据场景,生产环境的ID策略另说。

4.3 不同数据量级,造数策略完全不同

一千条、一万条、一百万条,策略绝对不是简单的参数放大的关系。千条级别,直接循环生成,写到Excel或者SQL文件都行。万条级别,注意批量插入,脚本本身保持简单即可。百万条级别,就要考虑生成器模式、批量刷写、关闭数据库自动提交、尽量用本地文件先行生成再导入数据库。

如果目标是压测,那就更讲究了。压测数据往往需要特定分布、特定关联、特定字段宽度,而且可能需要大量数据在同一个库里共存。这时候我一般会先把基础维度表造好,再在此基础上并行生成事实表数据,否则百万条数据全在一个脚本里跑,又慢又不稳定。

5. 真正难造的是“假但合理”的数据:三个翻车现场

5.1 翻车现场一:手机号全是“空号”

有一次给一个短信验证码功能造测试数据,我用随机9位数字拼在138后面生成了一堆手机号。看着挺正常,结果联调的时候短信服务商真去调了号码可携数据库,发现这批号码全是空号。虽然测试环境一般不会真的发短信,但那一瞬间我意识到,造数据不光是格式对,还要考虑你的下游系统会拿这些数据做什么。

后来我在造手机号的时候,就直接调用Faker的手机号生成器,并且尽量选用官方文档里的测试号段,或者刻意使用像19999999999这样不会打通的号段。你要清楚,造数据的目的不是真的能用这些手机号收短信,而是让你的业务逻辑跑得通,同时不要误伤真实用户。

5.2 翻车现场二:订单金额和明细对不上

这是个特别经典的问题。订单主表有一个总金额,订单明细表里有商品单价和数量,两者之间应该满足“总金额=明细行金额之和”的约束。当时我图省事,先造主表随机总金额,再造明细随机单价和数量,结果对账一跑,全是不平账。这种数据拿去做功能测试,不仅测不出问题,还会把正常代码判定为异常。

从此之后,遇到这种主子表结构,我的造数顺序一定是先造明细,再汇总主表金额,或者所有金额都从同一个根值派生出来。这种“从底向上”的造数顺序,能保证所有逻辑约束天然成立。

5.3 翻车现场三:用户画像自相矛盾

某次做一个用户画像页面,需要生成性别、年龄段、兴趣标签和消费类目。我图方便,独立随机每个字段,于是出现了“60岁女性,兴趣标签是电竞外设,消费类目全是什么潮牌男装”这种离谱画像。页面展示之后,产品经理一眼就看出数据是假的,问我为什么不做点合理的数据。

这给我提了个醒:造数据要定义“合理域”。比如根据年龄段限制兴趣标签和消费类目,或者只从一组“预设人设”里随机抽取。先设计二十个典型用户画像,再按画像去生成对应的属性数据,这样整体合理性就会好很多。演示效果也好,测试覆盖率也不差。

6. 造数据的进阶玩法:生产数据脱敏与“半造半真”混合

6.1 脱敏和造数其实是一回事

后来我逐渐发现,生产数据脱敏本质上就是一种造数。你要把真实的姓名、电话、地址替换成看起来合法的假数据,同时保留原本的统计特征和关联关系。比如把“张三”改成“张伟”,把手机号换成同一号段的随机号码,把身份证里的出生日期保留但把地址码和顺序码替换成虚拟值。这样既能测试真实业务逻辑,又不会导致敏感信息泄露。

如果团队已经有生产数据可以用,我的方案经常是“脱敏+补造”。从生产库抽取一小部分数据,做字段替换和偏移,比如日期整体往前后平移一段时间、金额加一个随机扰动、ID重新映射,这样既保留了数据分布的真实性,又切断了和真实用户之间的关联。

6.2 静态脱敏和动态脱敏怎么选

静态脱敏,就是把数据从生产库导出来后,清洗一遍再导入测试库。这种方式适合手工测试、数据开发、分析报表这类离线场景。动态脱敏,则是在请求数据的时候实时做脱敏处理,相当于中间加了一个拦截层,生产库不动,测试环境看到的是脱敏后的数据。这种方式对权限控制比较友好,但架构成本也更高。

如果你只是一个人维护测试库,我的建议是先做静态脱敏,简单直接,跑完就完事。如果你们有完整的测试环境治理要求,再考虑动态方案。脱敏工具方面,开源社区有很多选择,但在小团队里往往不需要上重型组件,用Python脚本或者SQL替换也能覆盖大部分需求。

6.3 造数据工具的取舍,我最后留下了什么

Faker确实是最常用的造数库,几乎所有基础字段都能覆盖。但是如果你的业务非常垂直,比如物流行业、医疗行业、金融行业,Faker提供的通用数据其实不够用。这时候我的做法是,在Faker之上封装一层“业务字段工厂”,把物流单号、医保目录编码、理财产品代码这些自定义规则都塞进去。核心思路就是:通用抽象用现成库,特性业务自己写规则。

还有一类场景需要“延续性数据”,比如一套数据要长期供多个环境使用。那你就不能每次运行脚本生成完全不同的数据,而要让生成结果可复现。方法是固定随机种子,或者在脚本里记录生成批次号。保持同批次数据在多个库之间的一致,很多联调和对比工作会轻松不少。

7. 关于“随便”两个字,我最后的看法

造数据这件事,越做到后面越觉得,它不是“随便”就能做好的,但也没必要做得像建设生产系统一样重。关键是让每一个字段都经得起逻辑推敲,让每一条数据都能为你下一步的开发验证提供有效支撑。

从最开始写一个几十行的随机脚本,到后来封装出一套带状态机、带分布逻辑、带脱敏能力的造数模块,我最大的体会是:数据生成规则的投入产出比极高。花一下午时间把规则写清楚,之后每次联调和测试都能省出好几天。尤其是那些被人反复吐槽的“真花时间”的环节,比如表单校验、报表核对、可视化看板,一套合理的数据能让你直接跳过大部分无效调试。

如果你现在正准备造数据,我的建议是四个字:先想后造。列一下业务有哪些表、哪些字段之间有依赖关系、哪些状态流转是合法的、哪些分布是符合常识的,再动手写代码。这个顺序省下来的时间,比你找任何库都值钱。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询