身份数据一致性是一条看起来多余、事后却最省钱的要求:同一条记录里的每个字段单看都合法,合在一起却讲不出一个说得通的人。这种数据最容易让人放松警惕,因为格式校验全部通过,界面显示漂亮,直到某个业务规则被悄悄绕过才暴露。本文讲清哪些字段必须互相咬合、不自洽的数据是怎么制造假通过的,以及这类校验放在哪一层最合适。
单看每个字段都对,合起来却说不通
一条记录由国家、地址、邮编、电话、出生日期、证件号与姓名拼成。单独检查时,每一栏都可能通过:邮编位数没错,电话位数没错,证件号长度也没错。问题出在它们彼此之间没有约束。
举例来说,地址写的是某个国家,电话号码的国家代码却是另一个地方的;邮编的形态属于一个国家,行政区名称却来自另一个国家;生日算出来的年龄是二十出头,记录里却自称是长期退休金领取人。这些矛盾不会触发任何单字段校验,却会让所有依赖这些字段组合的业务判断失准。
这类错误在手工造数据时格外常见,因为人是一栏一栏填的,注意力很难同时停在两条字段的关系上。字段越多,漏掉的关系就越多。
哪些字段必须属于同一个国家?
最基本的约束是「国家」这根轴:一条记录只能有一个国籍或者居住国,围绕它的所有字段都应当与这个国家相容。下表列出几组最常见的配对:
| 字段组合 | 一致性要求 | 破坏后的典型症状 |
|---|---|---|
| 地址与邮编 | 形态属于同一国家 | 合法邮编被判为非法 |
| 电话与地址 | 国家代码与所在国一致 | 短信类用例走到错误分支 |
| 证件号与国籍 | 号码形态与发行国相符 | 证件校验静默失效 |
| 生日与年龄门槛 | 算出的年龄与记录自述相符 | 门槛逻辑被绕过 |
| 姓名与语言 | 姓名形态不与该国规则冲突 | 展示与排序出现异常 |
这张表里的每一行都值得做成回归用例。它们的共同点是:单字段校验永远不会发现它们,而一旦跨字段校验缺失,测试就会在错误的假设上跑下去。
不自洽的数据为什么会造成假通过?
「假通过」指的是测试报告是绿的,但被测的行为实际上是错的。它发生的过程通常是这样:用例本来想验证「证件号不合规时应当拒绝」,而测试数据里的证件号恰好属于另一个国家的形态,于是校验分支根本没被走到,流程放行了,用例却因为断言写得太宽而通过。
反过来也会出现假失败:一条本应被接受的合法记录,因为里面的邮编来自另一个国家而被拒,测试报告红了一整天,最后发现是数据的问题而不是代码的问题。这两种情况都消耗真实的排查时间,而它们的根源都在数据本身不自洽。
要避免它,关键不是把数据造得更「标准」,而是让一致性约束在数据进入测试之前就生效。团队里最常见的分工是:数据由工具或固定装置提供,用例只负责断言,把拼字段这件事从手工环节拿掉。
校验该强到什么程度?
这里有一个真实的取舍:一致性规则太松,等于没有;太紧,会把合法输入挡在门外。世界上确实存在跨国家居住、跨国工作、同时持有两个国家证件的人,所以「电话国家码必须与居住国一致」这类规则在真实业务里需要留有例外。
比较稳妥的分层是:把强约束留给体系内部必然成立的配对,比如号码形态与它所属的编号体系;把弱约束留给现实里存在例外的配对,比如所在国与使用中的电话号码。弱约束的行为应当是提示与标记,而不是拒绝。
另外,同一批测试数据里也不该出现所有记录共享同一个字段值的现象:所有人同一天生日、所有电话前缀完全相同、所有地址都在同一座城市。这类「结构性一致」看起来没问题,实际上会让按这些字段分组的用例全部退化成同一个场景。合成数据只用于测试与演示,它不代表任何真实的人或住址,也不能拿去通过实名核验或者申请需要真实身份的服务,规则再严也改变不了这一点。
生成的先后顺序决定了会不会自相矛盾
上一条里的依赖顺序值得单独说清楚,因为它是绝大多数矛盾的直接来源:字段之间的关系是有方向的,先生成谁、后生成谁,决定了后面的人有没有依据可以遵循。
以一条完整的身份记录为例,国家必须最先确定,因为证件号的形态、电话的国家代码、邮编的结构都由它决定。地址与电话属于第二层,它们都依赖国家;出生日期与由它算出的年龄属于第三层,年龄必须从生日推导而不能独立取值;姓名与联系方式属于最后一层,它们和前面的字段关系最松。只要让每一层只能读取上一层的结论,矛盾就几乎不会出现。反过来,如果每个字段各抽各的随机数,再高的校验强度也拦不住几十个字段之间的组合错误。
批量生成时还有一处细节:一致性检查应当在写进数据库之前跑,而不是等下游的用例报错以后再回头找。把检查加在导出环节的收益最高,因为那时全部字段都还在手上,报错信息也能直接指出是哪一条记录、哪两个字段对不上。
给开发者:跨字段校验放在哪一层最合适
从下往上排,有三层可以放。第一层是数据库的列约束,它只适合位长与字符集这类单列规则,跨列的判断写进去会很快难以维护。第二层是写入前的领域校验,也就是在构造或保存一条记录时统一检查字段之间的关系,这一层最适合承载一致性规则,因为所有入口都会经过它。第三层是界面提示,它负责把问题尽早告诉填写的人,但不应当成为唯一的防线。
判断一条规则该放哪一层,可以问两个问题:违反它是否一定代表数据错误?如果是,放在领域校验;如果只是少见但合法,放在提示层。以及这条规则是否只服务于某一个页面?如果是,就不要提到全局,否则以后会被迫为它写例外。
生成器最容易破坏的正是这些配对。按字段独立随机生成时,国家换了而电话前缀没换、生日重抽了而年龄字段没重算,都属于同一类问题。能做的应对是在生成流程里维护一个依赖顺序:先定国家,再定地址与电话,再定生日与由它推导出来的年龄。上面的字段排布方式见一条身份记录要包含哪些字段那篇。
下一步
给测试数据加一致性检查最简单的一步,是先挑出上面那张表里与你业务最相关的两三行,写成断言跑在导入环节。要一批天然自洽的记录,直接去身份信息生成器按国家批量生成,字段之间的关系由工具保证;如果你在意的是这批数据在流程里怎么被复用,测试固定装置里的身份数据接着讲。